Combinatory Method Like Tap, But Able to Return a Different Value

Combinatory method like tap, but able to return a different value?

Define Object#as:

class Object
def as
yield self
end
end

And now you can write:

def not_sure_this_is_nice_enough_method1
something_complex(a, b, c).as do |obj|
a_predicate_check? ? obj.one_more_method_call : obj
end
end

Ruby: reuse value in a block without assigning it to variable (write object method on the fly)

The method simply yields self to the block, returning the block's result. I don't think it exists, but you can implement it yourself:

class Object
def decompose
yield self
end
end

[3, 4, 7, 8].decompose{ |array| array[2] + array[3] }
#=> 15

{:key1 => 'value', :key2 => true}.decompose{ |hash| hash[:key1] if hash[:key2] }
#=> "value"

[3, 4, 7, 8].decompose{ |array| [array.min, array.max] }
#=> [3, 8]

Difference between Kernel#yield_self, yield(self), Kernel#then and Object#tap in Ruby?

The difference between tap and yield_self is in what is returned by each of the two methods.

Object#tap yields self to the block and then returns self. Kernel#yield_self yields self to the block and then returns the result of the block.

Here are some examples of where each can be useful:

tap

Replacing the need for a result line at the end of a method:

def my_method
result = get_some_result
call_some_method_with result
result
end

can be written as:

def my_method
get_some_result.tap do |result|
call_some_method_with result
end
end

Another example is initialisation of some object that takes several steps:

some_object = SomeClass.new.tap do |obj|
obj.field1 = some_value
obj.field2 = other_value
end

yield_self and yield(self)

If used inside one of your own methods yield_self would have the same effect as yield(self). However, by having it as a method in its own right this promotes method chaining as an alternative to nested function calls.

This blog post by Michał Łomnicki has some helpful examples. e.g. this code:

CSV.parse(File.read(File.expand_path("data.csv"), __dir__))
.map { |row| row[1].to_i }
.sum

can be rewritten as:

"data.csv"
.yield_self { |name| File.expand_path(name, __dir__) }
.yield_self { |path| File.read(path) }
.yield_self { |body| CSV.parse(body) }
.map { |row| row[1].to_i }
.sum

This can aid with clarity where nested calls are being used for a series of transformations on some data. Similar features exist in other programming languages. The pipe operator in Elixir is a good one to take a look at.

then

#yield_self might feel a bit technical and wordy. That's why Ruby 2.6 introduced an alias for #yield_self, #then.

"something"
.then {|str| str.chars.map {|x| x.ord + 1 }}
.then {|ords| ords.map {|x| x.chr }}
.then {|chars| chars.join }
.then {|str| str + "!" }
.then {|str| str + "!" }
# tpnfuijoh!!

Cleave a run-time computed value?

cleave is likely not the right answer here. When Factor says cannot apply SOMETHING to a run-time computed value most often it means something can be written better. I think here you want to replace cleave with histogram:

IN: scratchpad 100 [ { "abundant" "deficient" "perfect" } random ] replicate 
histogram

--- Data stack:
H{ { "deficient" 33 } { "perfect" 30 } { "abundant" 37 } }

Is having a return type of Task enough to make a method run asynchronously?

Based on your comments you need to do something like this:

public Task<string> ComplexOperation()
{
return Task.Run(() => /* Do something complex */);
}

You can play with it like this:

public static async Task Main()
{
Console.WriteLine("Before" );
var myResultTask = ComplexOperation();
Console.WriteLine("After task creation");
var result = await myResultTask;
Console.WriteLine("After async await");
}

public Task<string> ComplexOperation()
{
Console.WriteLine("Creation");
return Task.Run(() =>
{
Console.WriteLine("In before work");
Thread.Sleep(500); //simulate work;
Console.WriteLine("In after work");
return "Done";
});
}

And compare the behavior with switching to your implementation when you just return Task.FromResult. Also it does not makes much sense in such test example, TBH.

How to tap an observable for only the first emitted value

You could do this with combination of tap() and map() because map() passes also index parameter for each next:

this.http.post()
.pipe(
map((response, index) => [response, index]),
tap(([response, index]) => {
if (index === 0) { ... }
}),
)
.subscribe(([response] => { ... })

You could of course put everything into map((response, index => {...}) and then just return the same response object but it's better to avoid making side-effects in map().

Equivalent of `std::iter::inspect` for method chains

You can define a trait for this almost exactly like that:

trait Inspect {
fn inspect(self, f: impl Fn(&Self)) -> Self;
}

impl<T> Inspect for T {
fn inspect(self, f: impl Fn(&Self)) -> Self {
f(&self);
self
}
}

I modified it a bit to pass self by value to make it more flexible. It will work with &T/&mut T and return &T/&mut T.

Test:

#[derive(Debug)]
struct Foo {}

impl Foo {
fn a(&self) -> &Self {
self
}
fn b(&self) -> &Self {
self
}
fn c(&self) -> &Self {
self
}
}

fn main() {
Foo {}
.a()
.inspect(|x| println!("after a(): {:?}", x))
.b()
.inspect(|x| println!("after b(): {:?}", x))
.c();
}

Output:

after a(): Foo
after b(): Foo

Playground


Test for mutable value:

#[derive(Debug, Default)]
struct Foo {
value: i32,
}

impl Foo {
fn a(&mut self) -> &mut Self {
self.value += 1;
self
}
fn b(&mut self) -> &mut Self {
self.value += 2;
self
}
}

fn main() {
Foo::default()
.a()
.inspect(|x| println!("after a(): {:?}", x))
.b()
.inspect(|x| println!("after b(): {:?}", x));
}

Output:

after a(): Foo { value: 1 }
after b(): Foo { value: 3 }

Playground

Building a Rails scope using `tap`

tap will not work for this.

  • all is an ActiveRecord::Relation, a query waiting to happen.
  • all.where(...) returns a new ActiveRecord::Relation the new query.
  • However checking the documentation for tap, you see that it returns the object that it was called on (in this case all) as opposed to the return value of the block.

    i.e. it is defined like this:

    def tap
    yield self # return from block **discarded**
    self
    end

    When what you wanted was just:

    def apply
    yield self # return from block **returned**
    end

    Or something similar to that.

This is why you keep getting all the objects returned, as opposed to the objects resulting from the query. My suggestion is that you build up the hash you send to where as opposed to chaining where calls. Like so:

query = {}
query[:first_name] = options[:query] if options[:query]
query[:graduated] = options[:graduated] if options[:graduated]
# ... etc.

all.where(query)

Or a possibly nicer implementation:

all.where({
first_name: options[:query],
graduated: options[:graduated],
}.delete_if { |_, v| v.empty? })

(If intermediate variables are not to your taste.)



Related Topics



Leave a reply



Submit