Blocks as Closures and Block Local Variables

In Ruby, blocks act like anonymous functions. After all, blocks carry a bunch of code, to be called only when yielded. A block also carries around the context in which it was defined:

 def​ chalkboard_gag(line, repetition)
  repetition.times { |x| puts ​"​​#{​x​}​​: ​​#{​line​}​​"​ }
 end
 
 chalkboard_gag(​"I will not drive the principal's car"​, 3)

This returns:

 0: I will not drive the principal's car
 1: I will not drive the principal's car
 2: I will not drive the principal's car

What’s the free variable here? It is line. That’s because line is not a block local variable. Instead, it needs access to the outer scope until it reaches the arguments of chalkboard_gag.

The behavior of the preceding code shouldn’t be too surprising, because it seems rather intuitive. Imagine now if Ruby didn’t have closures. The block then wouldn’t be able to access the arguments. You can simulate this by declaring line to be a block local variable by preceding it with a semicolon:

 def​ chalkboard_gag(line, repetition)
» repetition.times { |x; line| puts ​"​​#{​x​}​​: ​​#{​line​}​​"​ }
 end

Block local variables are declared after the semicolon. Now line in the block no longer refers to the arguments of chalkboard_gag:

 0:
 1:
 2:

Block local variables are a way to ensure that the variables within a block don’t override another outer variable of the same name. This essentially circumvents the variable capturing behavior of a closure.

Here’s another example:

 x = ​"outside x"
 1.times { x = ​"modified from the outside block"​ }
 
 puts x ​# => "modified from the outside block"

In this example, the outer x is modified by the block, because the block closes over the outer x, and therefore has a reference to it. If we want to prevent this behavior, we could do this:

 x = ​"outside x"
 1.times { |;x| x = ​"modified from the outside block"​ }
 
 puts x ​# => "outside x"

That covers most of what there is to know about block variables. In the next section, we take a look at different block patterns that are often seen in Ruby code. These patterns cover enumeration, resource management, and object initialization.

Next, let’s look at some patterns that use blocks, starting with enumeration.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.88.110