Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ruby use yield?

Tags:

ruby

I am new to Ruby. I have used a lot of C# and JavaScript which allow higher-order functions and I typically use them on a daily basis.

Ruby seems a little strange to me though. An each function might look like:

def each
    @items.each do |item|
        yield(item)
    end
end

items.each { |item| puts item }

Yet Ruby also has some support for higher-order functions. The above could be rewritten to something like:

def each(proc)
    @items.each do |item|
        proc.call item
    end
end

items.each -> (item) { puts item }        # Or...
items.each lambda { |item| puts item }

Or even:

def each(&proc)
    @items.each do |item|
        proc.call item
    end
end

# No difference in syntax.
items.each { |item| puts item }

Which is more on par with most other languages, and is just a few characters longer. Instead of explicitly passing in a block, everything seems to use yield.

yield itself seems crazy, magical, and mysterious. After all, it's going to the origin of the call and grabbing a block immediately following the call. This seems bizarre and unnatural, and I'm not aware of any parallel of this feature in another language.

So what's the deal with yield?

like image 894
mka Avatar asked Jan 14 '13 00:01

mka


4 Answers

Yield is Syntax Sugar

This example of yield:

def do_something_for_each(array)
  array.each do |el|
    yield(el)
  end
end

Is just syntax sugar for:

def do_something_for_each(array, &block)
  array.each do |el|
    block.call(el)
  end
end

Pick the syntax you like and run wild with it.

like image 127
yfeldblum Avatar answered Oct 12 '22 02:10

yfeldblum


Yield Passes Objects to a Method's Block

[Yield is] going to the origin of the call and grabbing a block immediately following the call.

Not really. yield passes an argument to a block; it doesn't "grab a block" or do anything with it. In other words, this:

def foo; yield self; end
foo { |x| x.inspect }                                       
# => "main"

Here, yield isn't doing anything but passing an argument to the block that is passed into the foo method. Every Ruby method supports an optional block—except when a block is actually mandatory—so the only "magic" is that the language allows a block to be passed even when it isn't explicitly declared as part of the method signature.

Further Examples

To see this implicit signature in action, consider this:

def foo; puts block_given?; end
foo { |x| x.inspect }

which will print "true" and return nil, which is the expected return value from the puts method.

Of course, without yield the block doesn't do anything at all. For example:

def foo; end
foo { |x| x.inspect }
# => nil
like image 44
Todd A. Jacobs Avatar answered Oct 12 '22 02:10

Todd A. Jacobs


One advantage of yield is it also lets you use next (like continue) and break. In other languages, for next, you might have to use return, and for break, you might have to (ab)use exceptions. It is arguably nicer to have built-in support for these sorts of operations.

like image 4
icktoofay Avatar answered Oct 12 '22 03:10

icktoofay


In most cases, you execute the block right there in the method, using yield.

The block is passed straight into the method, and the method can then call back to the block with the yield keyword.

def a_method(a, b)
a + yield(a, b)
end
a_method(1, 2) {|x, y| (x + y) * 3 } # => 10

When you call back to the block, you can provide values for its arguments, just like you do when you call a method. Also, like a method, a block returns the result of the last line of code it evaluates.

like image 1
Rameshwar Vyevhare Avatar answered Oct 12 '22 04:10

Rameshwar Vyevhare