Lately, I’ve been quickly wrapping my head around Ruby– that seductive language that is quickly becoming the very reason I want to get out of bed in the morning. In my attempt to grok Ruby Blocks, I started writing this sort of “if you can teach it, then you know it” series called Block Party. As such, it might help someone who stops by to read it, but it’s fundamentally written to help me.
Because I’m a long-time Pythonista, I decided to come to Ruby blocks from a Python perspective, and discussed Python’s Lambda construct in the last post in this series. In this one, I dive full on into a basic discussion of Ruby blocks, continuing where that post left off.
We all need closure
Time for as little Computer Science as possible. Python lambdas and Ruby blocks are implementations of a construct called a Closure. There’s a great deal to be said about what this actually means, but the name itself hints at all we really need to know about the warm and chewy parts.
In a “boil it down so you can explain it to an 8th grader” way a closure is this: a little piece of something that will wrap itself around something else. Like a blanket. A blanket can be a closure. I take a blanket, send it an 8th grader, and the blanket can wrap itself around the 8th grader and do what it does, keep them warm.1
Okay, examples. Here’s one:
def tuck_in
kids.each_one do |kid|
blanket.wrap(kid)
end
end
Simple right? We make a little “tucking in” method. For each kid in our list of kids, wrap each one in a blanket.
Wait! That’s just a loop!
Well, almost. Technically, it’s a closure. Let’s go back to Python for a second to compare the two:
def tuck_in:
for kid in kids:
blanket.wrap(kid)
Same thing, right? Wrong.
Our Python method actually calls the kid and makes the kid come to us to get wrapped up. It’s saying “call each kid, bring them here, wrap them in a blanket.”
The Ruby method is different. It actually goes to the kids. The Ruby method says “Take this ‘blanket wrap’ idea, and send it to each of the kids individually.”
What the hell am I talking about? Patience, young padawan.
A Question of Scope
In order for us to understand what I’m talking about, we need to do a bit of implementation. Let’s implement our “kids” type. Technically, kids is just a list of all the kids we have, we don’t really need to implement an array to do this, but it’ll explain a lot:
class Kids < Array
def each_one
self.each_with_index do |n, i|
yield(n)
end
end
end
kids = Kids.new
%w(Wendy Michael John).each{|n| kids << n}
kids.each_one do |n|
p "Tucking in #{n}"
end
Okay, what are we doing? Well, it’s pretty silly and contrived, but good for an example.
We created a subclass of Array with a special iterator. That iterator has a ‘yield’ call, which makes it a block. After doing this, we grabbed an instance of our new class, populated it with some kids, and then for each kid, we tucked them in. Output?
"Tucking in Wendy" "Tucking in Michael" "Tucking in John"
Simple. But wait, there’s more!
This is where the idea of context comes in. Let’s say that, everytime we tuck a kid in, we want to tell them the name of the last kid we tucked in. “I just tucked in Wendy, so I’m going to tuck you in now.” Let’s try it:
last_kid = nil
kids.each_one do |n|
p "just tucked in #{last_kid}"
p "Tucking in #{n}"
last_kid = n
end
Of course, this does what we want, but it’s not elegant. The issue is that we have to store the context of the iterator in a secondary variable and futz around with it. Basically, we’re required to store the last child in this scope, even though that information may not be appropriate for this scope.
This is why Ruby blocks are so sweet. Because with a block, we’re not calling the kids and telling them to come to us, we’re sending the tucking in method to the kids. Because of this, you don’t need to store the name of the last kid locally.
Check out this awesome sauce (hint: It’s the same as above, with two extra print statements):
class Kids < Array
def each_one
self.each_with_index do |n, i|
yield(n)
p "Going now to tuck in #{self[i+1]}" if i < (self.length - 1)
p "Everyone all snuggled up!" if i == (self.length - 1)
end
end
end
kids = Kids.new
%w(Wendy Michael John).each{|n| kids << n}
kids.each_one do |n|
p "Tucking in #{n}"
end
Okay, output?
"Tucking in Wendy" "Going now to tuck in Michael" "Tucking in Michael" "Going now to tuck in John" "Tucking in John" "Everyone all snuggled up!"
Everything in its place
The reason this is so awesome is that in our first attempt at remembering the last child’s name, we had to figure out a way to store it locally, even though that information is out of the local context. Furthermore, consider how difficult it would be to figure out the name of the next child.
With Ruby blocks, the logic we send is contained within the context of the enclosing construct. In otherwords, the “tucking in” call is not local to the toplevel, it’s local to the Kids::each_one method– in otherwords, it’s local to Kids.
It’s a bit to wrap your head around, but the basic story is that we are trying to keep everything in its place. We don’t want to create temporary variables to hack out a way to track which kid we just came from, or which kid we’re going to. That’s not elegant, and it’s even error prone. Rather, we want that information tracked within the appropriate scope, that way, we keep things nice and simple on the outside, and warm and chewy on the inside. With this kind of construct, we can safely do anything we want to the kids, and know that figuring out who the next kid is, or who the last kid was, can be taken care of. It minimizes our code, simplifies it, and keeps the logic separated in its appropriate place.
In the next post, my plan is to return to the Stream Reach example of the previous post to show, with an even more complex example, why this is such a beautiful thing.
- Well, technically, the 8th grader would wrap the blanket around themselves. Maybe not the most appropriate analogy. [↩]


