What is a block in ruby?

Exploring anonymous functions

In Ruby, blocks, as well as Procs and lambdas, are all self contained, anonymous functions. Might sound confusing, but let’s start with an example. If you were in a math class and your teacher told you to “write the function that takes whatever is input (for instance the variable x) and multiplies it by two”, you’d probably come up with a solution like f(x) = x · 2.

Now in ruby we could have written a function like

def mult_by_two(x)
x * 2
end

But this is quite verbose just to explain the simple mathematical construct of f(x) = x · 2.

In comes the Proc/lambda (they are technically distinct, but their distinction is very minute and subtle, so consider them the same for right now). I can open up pry and write down a Proc which has the same behavior:

[1] pry(main)> -> (x) { x * 2 }

Which you can read as f(x) = x · 2. Now the awesome thing is now I can assign this arbitrary and anonymous function to a variable and pass it around as if it were data.

[2] pry(main)> fx = -> (x) { x * 2 } 
#<Proc:0x007ff6046dd9c0@(pry):1 (lambda)>

Note: I can’t use just f because of name collisions in ruby/pry.

I can even execute the function given an arbitrary input. I just use the variable I’ve bound it to, and invoke the proc with [parameter]

[3] pry(main)> fx[7]
14
[4] pry(main)> fx[42]
84
[5] pry(main)> fx[“hello”]
“hellohello”

Note: This only works with strings because we did x · 2, not 2 · x

Now what is a block? A block is a special form of a self contained function that exists in ruby. You can think of it as a special form of a Proc.

Notice how we were able to assign the Proc to a variable? That’s because a Proc is an actual object. A block, on the other hand, is just a piece of syntax sugar for when you are writing a method definition. For instance, let’s make our own fake array-like class (that only takes 3 elements) and implements its own #map method[1] .

class ThreeElementArray
def initialize(first, second, third)
@first = first
@second = second
@third = third
end
 def map
ThreeElementArray.new(
yield(@first),
yield(@second),
yield(@third)
)
end
end

Which would then be used like

[6] pry(main)> ThreeElementArray.new(1, “hello”, 42).map do |element|
[6] pry(main)* element * 2
[6] pry(main)* end
#<ThreeElementArray:0x007ff60476dfc0 @first=2, @second=”hellohello”, @third=84>

What this really breaks down into is

ThreeElementArray.new(1, “hello”, 42).map

which is the method invocation. And:

do |element|
element * 2
end

which is the block it was given.

yield is the special keyword in the method definition that states “take the block that this method is called with, and execute that block with what I passed in as the parameter to the block”. So in pseudocode, the three yields in sequence behave like:

# yield(@first) and @first = 1
do |1|
1 * 2
end
# yield(@second) and @second = "hello"
do |"hello"|
"hello * 2"
end
# yield(@third) and @third = 42
do |42|
42 * 2
end

A second look at the block we gave to the #map:

do |element|
element * 2
end

This should look really familiar, because it’s a more verbose version of the Proc we defined earlier, -> (x) { x · 2 }!

We can even use a Proc in something like map if we first convert it to a block. We can do this by prefixing it with &, so for instance

[7] pry(main)> ThreeElementArray.new(1, “hello”, 42).map(&fx)
#<ThreeElementArray:0x007ff603998c10 @first=2, @second=”hellohello”, @third=84>

Which you’ll notice has the same output as before with the do end block.

So to summarize: Procs, lambdas, and blocks, are ways of grouping arbitrary pieces of code. They are also known as anonymous functions, or specifically closures, which I hope to explain further in a future blog post. Procs are actual objects in ruby, whereas blocks are just syntax sugar around passed in Procs in ruby’s method definitions.


Recommended readings

Show your support

Clapping shows how much you appreciated Christopher Hendrix’s story.