Closures in Ruby

Joshua Lacey

The other day I was reading through some ruby code and I saw a method that was defined with “&block” as an argument. Being a beginner I was pretty familiar with the #each and #map methods and I knew that they accepted a block as an argument, but I did not remember how to define a method that accepts a block.

So what is a block? A block is basically a way of grouping code so that it can be run.

a = { "im a block" }       #=> SyntaxErrordef new_method(&block)
yield
end
new_method{ "im a block" } #=> "im a block"new_method do "im a block" end #=> "im a block"

Blocks can’t be assigned to a variable and only work as part of the syntax of a method. The #new_method is given an argument of a block using the & operator. The yield keyword tells the #new_method to yield the contents of the block that has been passed in.

Procs

Procs are like blocks except that they can be assigned to variables. A proc is a ruby object and can be instantiated by calling ‘Proc.new’. Ruby gives us a little easier way of doing the save thing by just calling ‘proc’ and passing it a block. Instead a proc can yield it’s contents by using the #call method.

some_proc = Proc.new { "im a proc" }also_a_proc = proc { "im also a proc" }some_proc.call      #=> "im a proc"also_a_proc.call    #=> "im also a proc"

A Proc can be written to accept arguments just like a block.

who = proc {|x| "Hello my name is #{x}"}who.call("Proc")      #=> "Hello my name is Proc"

And if you don’t like having to write out ‘.call’ every time ruby gives you a bunch of different ways you can write the same thing.

who.("Proc")       #=> "Hello my name is Proc"
who::("Proc") #=> "Hello my name is Proc"
who["Proc"] #=> "Hello my name is Proc"

Lambdas

Lambdas in ruby belong to the proc class. They are similar in functionality to a proc but have some key differences. First lets go through a lambda is created.

lamb = lambda {|x| x + 1 }       #=> <Proc:0x007fa3762f48d0@(irb):31 (lambda)>lamb.call(2)                 #=> 3
lamb.class #=>Proc
lamb.lambda? #=>true
who.lambda? #=>false
  1. The first difference between a lambda and a proc is that a lambda is very particular about how many arguments you give it. Procs seem like they could care less about args.
my_lamb = lambda {|x| x }my_lamb.call("hey")   
#=> "hey"
my_lamb.call("hey", "baby")
ArgumentError: wrong number of arguments (given 2, expected 1)
proc_you = proc {|x| x }proc_you.call("no")
#=> "no"
proc_you.call("no", "but maybe yes")
#=> "no"
proc_you.call
#=> nil

2. The second key difference is how they deal with returns.

def patty_says
proc {return "Let's be friends forever"}.call
return "nevermind"
end
patty_says #=>"Let's be friends forever"def janice_says
lambda {return "Let's be friends forever"}.call
return "nevermind"
end
janice_says #=> "nevermind"

The method containing a proc returns the return value of the called proc, but the lambda let’s the method keep it’s return value. This can be really important when you’re writing your functions because if you used a proc in this instance none of the rest of the code in the method would be executed. There is a next keyword that will let the truth of the method come out even though the proc is called.

def bobby_says
proc { next "Let's be friends forever" }.call
return "nevermind"
end
bobby_says #=> "nevermind"

Conclusion

So like why though... Suppose we wanted to create a new method for the Array class. We’ll call this method ‘everyother’. We want this method to accept a block just like #each and #map and we want it to return a new array just like #map.

class Array
def everyother(&block)
new_array = []
i = 0
while i < self.size
new_array << block.call(self[i])
i +=2
end
new_array
end
end
[1,2,3,4].everyother{|x| x + 1 } #=> [2, 4]

Instead of having to write out a block every time we can pass a proc or a lambda to the function.

pi = proc {|x| x * 3.14159265359}[1,2,3,4].everyother(&pi)#=> [3.14159265359, 9.424777960770001]plus_two = lambda { |x| x + 2 }[1,2,3,4].everyother(&plus_two)#=> [3,5]

Blocks, Procs, and Lambdas are known as closures in ruby. While the concept of closures is not unique to ruby they allow ruby programers elegant ways of abstracting code.

Joshua Lacey

Written by

Fullstack Webdeveloper: Javascript, Node.js, React.js, Ruby on Rails

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade