Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Proc#call vs yield

Tags:

yield

ruby

What are the behavioural differences between the following two implementations in Ruby of the thrice method?

module WithYield   def self.thrice     3.times { yield }      # yield to the implicit block argument   end end  module WithProcCall   def self.thrice(&block)  # & converts implicit block to an explicit, named Proc     3.times { block.call } # invoke Proc#call   end end  WithYield::thrice { puts "Hello world" } WithProcCall::thrice { puts "Hello world" } 

By "behavioural differences" I include error handling, performance, tool support, etc.

like image 883
Sam Stokes Avatar asked Sep 11 '09 10:09

Sam Stokes


People also ask

What is Ruby Proc?

A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called. Proc is an essential concept in Ruby and a core of its functional programming features.

What is proc and lambda in Ruby?

Blocks are syntactic structures in Ruby; they are not objects, and cannot be manipulated as objects. It is possible, however, to create an object that represents a block. Depending on how the object is created, it is called a proc or a lambda.

What is the difference between block proc and lambda in Ruby?

When using parameters prefixed with ampersands, passing a block to a method results in a proc in the method's context. Procs behave like blocks, but they can be stored in a variable. Lambdas are procs that behave like methods, meaning they enforce arity and return as methods instead of in their parent scope.

What is the difference between proc and lambda in rails?

In Ruby, a lambda is an object similar to a proc. Unlike a proc, a lambda requires a specific number of arguments passed to it, and it return s to its calling method rather than returning immediately. proc_demo = Proc.


1 Answers

I think the first one is actually a syntactic sugar of the other. In other words there is no behavioural difference.

What the second form allows though is to "save" the block in a variable. Then the block can be called at some other point in time - callback.


Ok. This time I went and did a quick benchmark:

require 'benchmark'  class A   def test     10.times do       yield     end   end end  class B   def test(&block)     10.times do       block.call     end   end end  Benchmark.bm do |b|   b.report do     a = A.new     10000.times do       a.test{ 1 + 1 }     end   end    b.report do     a = B.new     10000.times do       a.test{ 1 + 1 }     end   end    b.report do     a = A.new     100000.times do       a.test{ 1 + 1 }     end   end    b.report do     a = B.new     100000.times do       a.test{ 1 + 1 }     end   end  end 

The results are interesting:

      user     system      total        real   0.090000   0.040000   0.130000 (  0.141529)   0.180000   0.060000   0.240000 (  0.234289)   0.950000   0.370000   1.320000 (  1.359902)   1.810000   0.570000   2.380000 (  2.430991) 

This shows that using block.call is almost 2x slower than using yield.

like image 173
jpastuszek Avatar answered Sep 20 '22 23:09

jpastuszek