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!!

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)