Difference between various high order functions in Ruby

Freya Arakaki

Hello again~ ❀

In my previous post, different syntax of passing a functions are discussed. Now let's discussed about what the differences they have!

⭐️ if you haven't read the first post, head over here ⭐️

Discussion on the subtle differences

Let's have a quick recap of the different methods to pass a function in Ruby:

  • block: do...end
  • Proc: Proc.new do...end
  • lambda: lambda do...end
  • method: define a function and pass using method(:function_name)
(a) Storing into a variable

Let's consider the first criteria. Which of them can be stored into a variable. For example in Proc's case, it can be stored in a variable like this:

store_proc_in_a_variable.rb

proc = Proc.new do |n| puts "***" + n + "***" end

On the other hand, we cannot do that using block:

block-syntax-error.rb

block = do |n| puts "***" + n + "***" end

The above will trigger a syntax error.

Here's a summary on various ways of function, and which can be stored in a variable:

Description Block Proc Lambda Method
Storing into a variable No Yes Yes Yes

Only block is not able to be stored into a variable, the rest is possible.

(b) How they response to 'return'
Description Block Proc Lambda Method
How they
response
to 'return'
N/A not scoped scoped scoped

When doing a return in Proc, it is not scoped to it's own closure, the return call will actually return out of the parent method. On the other hand, lambda and method will be scoped and return in it's own closure. Take a look at the below code and you will understand:

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, because Proc return out of the parent method.

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

Let's examine what class each of them belong to. By running the code below we can see that all of them came from Proc except for method.

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. For lambda and method(:func), they give error right away. Let's take a look at the code.

For block shown below, even wrong number of argument is supplied, the output can still be printed.

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, it is the same:

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, we will get a syntax error:

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

arg_check_lambda.rb:9: wrong number of arguments (1 for 2) (ArgumentError) from arg_check_lambda.rb:6:in `call' from arg_check_lambda.rb:6:in `argument_check_wrong' from arg_check_lambda.rb:11 proc: arguments received: a1, String

For method(:func), we will also get a syntax error:

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

arg_check_method.rb:6:in `func': wrong number of arguments (1 for 2) (ArgumentError) from arg_check_method.rb:6:in `call' from arg_check_method.rb:6:in `argument_check_wrong' from arg_check_method.rb:14 method(:func): arguments received: a1, String

Summary

By understanding the subtle differences, we can choose the most suitable one to use. I hope this is helpful for you. 😃

Have a nice day~✿~!

/@doryfish
Written By

Freya Arakaki

Like coffee, love Ruby, enjoy baking stuff when I am not coding ~♥
Published in Ruby
Enjoyed the post?

Clap to support the author, help others find it, and make your opinion count.