Sonntag, 28. April 2013

Lambda is not a Proc

I guess you already know about Procs. The lambda in Ruby is also a closure. Therefore, it encapsulates logic which can be executed at the time it is encountered. It even an be put into a particular context by passing it to a function. So far nothing new.
That's why I start with explaining the lambda and later compare with the Proc.
A simple lambda:
my_lambda = lambda { puts "A lambda" }
my_lambda.call
returns:
A lambda
Sure, you can pass a parameter:
my_lambda = lambda { |param| puts "Parameter: #{param}" }
my_lambda.call(1)
returns:
Parameter: 1
And yeah you can also pass more than one parameter like:
my_lambda = lambda { |p1, p2| puts "Parameters: #{p1}, #{p2}" }
my_lambda.call(1, 2)
returns:
Parameters: 1, 2
You also can pass the lambda to a method for some reasons:
def my_function(block)
  puts "before lambda"
  block.call
  puts "after lambda"
end
my_lambda = lambda { puts "inside lambda" }
my_function my_lambda
returns:
before lambda
inside lambda
after lambda
Though there are not many reasons for passing logic to an object method without using data of that object. You would more often want to "send logic" to an object for dynamically using its data. Then you pass a parameter like:
def my_function(block)
  number = 0
  block.call(number)
end
my_lambda = lambda { |p| puts "inside lambda: #{p += 1}" }
my_function my_lambda
returns:
inside lambda: 1
At this point I note an essential property of closures. They automatically carry with them the bindings from the code location where they were created.
 
word = "Foo"
def redefine_word(block)
  word = "Bar"
  puts word
  block.call
end
redefine_word(lambda { puts word })
You really have to take care of the scope, when you send a block to a function. Consider the output:
Bar
Foo

The difference between lambda and Proc

There is another exciting detail I want to put the finger on. Control keywords like return, raise, break, redo, retry do NOT have any impact on the further execution. And that's an important difference to Procs. A lambda example:
 
def my_method
  puts "before lambda"
  my_lambda = lambda do
    puts "inside lambda"
    return
  end
  my_lambda.call
  puts "after lambda"
end
my_method
returns:
before lambda
inside lambda
after lambda
And now the same snippet with a Proc:
 
def my_method
  puts "before Proc"
  my_proc = Proc.new do
    puts "inside Proc"
    return
  end
  my_proc.call
  puts "after Proc"
end
my_method
returns:
before Proc
inside Proc
Boom! The "return" inside the Proc prevents executing all following logic (line 8 of "my_method"). Good to know, isn't it? Another important difference concerns the parameters of the block. Lambdas are strict:
my_lambda = lambda { |p1, p2| puts "Parameters: #{p1}, #{p2}" }
my_lambda.call("One")
ends up in an exception:
wrong number of arguments (1 for 2) (ArgumentError)
But a Proc is liberal:
my_proc = proc { |p1, p2| puts "Parameters: #{p1} and #{p2}" }
my_proc.call("One")
returns:
Parameters: One and
Keep the differences in mind, when you choose a Proc or a lambda for closuring.

Supported by Ruby 1.9.3