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!

2. Proc.new

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!

3. Lambda

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!

4. method(:some_method)

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

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!

Referring to the code right above this sentence, we can see that for 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

On the other hand, lambda_return & method_return work just like a normal method call, so the second sentence appeared.

(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

(d) Check for correct number of argument
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

For proc:

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

For lambda:

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

For method(:func):

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

More Reading Material:
http://blog.bigbinary.com/2013/03/12/understanding-instance-exec-in-ruby.html

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s