Ruby Block vs Proc.new vs Lambda vs method(:func) Summarized
There are many ways to pass code around in Ruby, so today I'm going to make a comparison between the 4 different ways. The first section, I will show the syntax of using each of them:
1. Block
2. Proc
3. Lambda
4. method(:func)
On the second section, I will compare the subtle differences they have.
A quick syntax comparison:
* note that the result is at the last section of each code *
1. Block
class String | |
def perform | |
yield self | |
end | |
end | |
#=== block ==== | |
block_string = "Hey there, " | |
block_string.perform do |n| | |
puts n + "from a block!" | |
end | |
#=== Result === | |
Hey there, from a block! |
class String | |
def perform code | |
code.call self | |
end | |
end | |
#===proc: storing into a variable==== | |
proc_string1 = "Hey there, " | |
proc_code = Proc.new do |n| | |
puts n + "from a proc storing into a variable!" | |
end | |
proc_string1.perform proc_code | |
#===proc: calling like a block==== | |
proc_string2 = "Hey there, " | |
proc_string2.perform( Proc.new do |n| | |
puts n + "from a proc calling like a block!" | |
end) | |
#=== Result === | |
Hey there, from a proc storing into a variable! | |
Hey there, from a proc calling like a block! |
class String | |
def perform code | |
code.call(self) | |
end | |
end | |
#===lambda method==== | |
lambda_string = "Hey there, " | |
lambda_code = lambda do |n| | |
puts n + "from a lambda!" | |
end | |
lambda_string.perform lambda_code | |
#=== Result === | |
Hey there, from a lambda! |
class String | |
def perform code | |
code.call(self) | |
end | |
end | |
#=== method ==== | |
method_string = "Hey there, " | |
def func n | |
puts n + "from a method!" | |
end | |
method_string.perform method(:func) | |
#=== Result === | |
Hey there, from a lambda! |
Discussion on the subtle differences:
The table below summarizes what each of them can and cannot do, under the table, there are a more in depth explanation on the things shown in the table.
Description | Block | Proc | Lambda | Method | Proof |
Storing into a variable | No | Yes | Yes | Yes | See (a) |
How they work | N/A | Code Replacement | Method Call | Method Call | See (b) |
What class they belong to | Proc | Proc | Proc | Method | See (c) |
Check for correct number of argument | No | No | Yes | Yes | See (d) |
(a) Storing into a variable
Only block is not able to be stored into a variable, the rest is possible, refer to the quick syntax section above.
(b) How they work
def proc_return | |
Proc.new { return "proc1"}.call | |
return "proc2 I AM HERE!" | |
end | |
def lambda_return | |
lambda { return "lambda1" }.call | |
return "lambda2 I AM HERE!" | |
end | |
def method_return | |
method(:func).call | |
return "method2 I AM HERE!" | |
end | |
def func | |
return "method1" | |
end | |
puts proc_return | |
puts lambda_return | |
puts method_return | |
#=== Result === | |
proc1 | |
lambda2 I AM HERE! | |
method2 I AM HERE! |
proc_return
, the line return "proc2 I AM HERE!"
is never executed, this is because, Proc.new{return "proc1}.call
works like code replacement, we can imagine proc_return
to be like this:def proc_return | |
return "proc1" | |
return "proc2 I AM HERE!" | |
end |
(c) What class they belong to
Run this code:
def what_class(&code) | |
return code.class | |
end | |
def func | |
"nothing" | |
end | |
puts (what_class do end) | |
puts Proc.new {}.class | |
puts lambda{}.class | |
puts method(:func).class | |
#=== Result === | |
Proc | |
Proc | |
Proc | |
Method |
Block and Proc don't check for the correct number of arguments, they discard extra parameters silently.(http://www.ruby-doc.org/core-1.9.3/Proc.html#method-i-call). For lambda and method(:func), they give error right away. Here's the code
For block:
def argument_check_correct(&code) | |
code.call("a1","a2") | |
end | |
def argument_check_wrong(&code) | |
code.call("a1") | |
end | |
argument_check_correct do |a1,a2| | |
puts "block: arguments received: #{a1}, #{a2.class}" | |
end | |
argument_check_wrong do |a1,a2| | |
puts "block: arguments received: #{a1}, #{a2.class}" | |
end | |
#====== result ========= | |
block: arguments received: a1, String | |
block: arguments received: a1, NilClass |
def argument_check_correct(code) | |
code.call("a1","a2") | |
end | |
def argument_check_wrong(code) | |
code.call("a1") | |
end | |
proc1 = Proc.new{|a1,a2| puts "proc: arguments received: #{a1}, #{a2.class}"} | |
argument_check_correct proc1 | |
argument_check_wrong proc1 | |
#====== result ========= | |
proc: arguments received: a1, String | |
proc: arguments received: a1, NilClass |
def argument_check_correct(code) | |
code.call("a1","a2") | |
end | |
def argument_check_wrong(code) | |
code.call("a1") | |
end | |
lambda1 = lambda {|a1,a2| puts "proc: arguments received: #{a1}, #{a2.class}"} | |
argument_check_correct lambda1 | |
argument_check_wrong lambda1 | |
#====== result ========= | |
/Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_lambda.rb:9: wrong number of arguments (1 for 2) (ArgumentError) | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_lambda.rb:6:in `call' | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_lambda.rb:6:in `argument_check_wrong' | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_lambda.rb:11 | |
proc: arguments received: a1, String |
def argument_check_correct(code) | |
code.call("a1","a2") | |
end | |
def argument_check_wrong(code) | |
code.call("a1") | |
end | |
def func (a1,a2) | |
puts "method(:func): arguments received: #{a1}, #{a2.class}" | |
end | |
argument_check_correct method(:func) | |
argument_check_wrong method(:func) | |
#====== result ========= | |
/Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_method.rb:6:in `func': wrong number of arguments (1 for 2) (ArgumentError) | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_method.rb:6:in `call' | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_method.rb:6:in `argument_check_wrong' | |
from /Users/tanjunrong/OnlineU/SaaS/Assignments/TestSpace/arg_check_method.rb:14 | |
method(:func): arguments received: a1, String |