Define Closures (Best Tutorial 2019)

 

Define Closures

Define What are Closures

This tutorial defines what is closure with best examples. A closure is a function whose body references a variable that is declared in the parent scope. Furthermore, you can transfer these concepts to other languages that support closures, such as JavaScript, Haskell, and Elixir.

 

Once you get comfortable with the basic concepts, you’ll explore two interesting techniques made possible by using closures. First, you’ll learn that you don’t really need classes at all.

 

With some lambdas sprinkled with variables, you can replicate much of the functionality that classes provide. Second, you’ll see how closures let you write decoupled code using callbacks.

 

Finally, you’ll put this newfound knowledge to use by implementing your own version of Ruby’s Enumerable #reduce method using lambdas, a Ruby language feature that lets you write code in a functional-like programming style.

 

The Foundations of a Closure

 Closure Foundations

Lexical scoping is sometimes known by another term— static scoping. I guess these terms are more catchy than “eyeball scoping.”

 

Let’s take a look at an example. Open IRB and follow along with the following example:

>> msg = "drive the principal's car" => "drive the principal's car"
>> 3.times do
>> prefix = "I will not"
>> puts "#{prefix} #{msg}"
>> end
I will not drive the principal's car
I will not drive the principal's car
I will not drive the principal's car

do .. end creates a new scope. Within the block, the prefix is declared and has the value "I will not". msg is more interesting.

 

It is not declared within the block but in the outermost, or parent, scope. The inner scope has access to the parent scope. Because of this, msg continues to have the value "drive the principal’s car"

 

You have just seen that the inner scope has access to the parent scope. Does the opposite hold true? For example, can prefix be accessed outside of the block? You can easily find out using IRB again:

>> puts prefix

NameError: undefined local variable or method `prefix' for main:Object from (IRB):6

 

Identifying Free Variables

Free Variables

A free variable is a variable that is defined in a parent scope. Let’s look at an example that will make this clearer. We’ll modify the program we just wrote to use lambdas instead. Lambdas are Ruby’s version of anonymous functions found in other languages. Type this program into your IRB session:

>> chalkboard_gag = lambda do |msg|
>> lambda do
>> prefix = "I will not"
>> "#{prefix} #{msg}"
>> end
>> end
=> #<Proc:0x007ffa2f901600@(IRB):1 (lambda)>

 

Instead of seeing the output, the session shows that the return value is a lambda "drive the principal’s car" that is represented by a Proc object. Don’t worry about this for now; we’ll come back to this soon. Instead, try to imagine how you would make the chalkboard_gag variable return It might not be as straightforward as you think!

 

The trick here is to tease apart chalkboard_gag layer by layer. The first layer is the outermost lambda:

» chalkboard_gag = lambda do |msg| lambda do
prefix = "I will not" "#{prefix} #{msg}"
end
» end
The outermost lambda takes a single argument, msg. But what does it return?
Another lambda. Let’s turn our attention to the inner lambda:
chalkboard_gag = lambda do |msg|
» lambda do
prefix = "I will not"
"#{prefix} #{msg}"
» end
end

The body of the inner lambda declares the prefix variable. On the other hand, msg is not declared anywhere in the lambda’s body. Where is it declared then? It’s declared in the parent scope as the argument of the outer lambda. This makes msg a free variable.

 

The parent scope is also called the surrounding lexical scope because the outer lambda wraps around the inner one. It is this wrapping around that allows the inner lambda to access variables declared in the outer one.

 

Let’s put chalkboard_gag to the test. Go back to IRB where you last created the chalkboard_gag lambda. Then supply a value to the outermost lambda. Invoke the lambda by using the call method:

>> inner_lambda = chalkboard_gag.call("drive the principal's car") => #<Proc:0x007fca608589a0@(IRB):2 (lambda)>

 

As expected, the return result is lambda. To get to the final result, you need to invoke the inner lambda, which you assigned to inner_lambda. To invoke it, use the call method:

>> inner_lambda.call()
=> "drive the principal's car"

 

Whenever an inner lambda refers to a variable that is not declared within it, but that variable is declared in the parent scope of that lambda, that is a free variable. 

 

At this stage, you almost have all the tools and knowledge needed to point out a closure. You know what lexical scoping is and how it works. You also know how to identify a free variable. Now, you need to know what separates closures and non-closures.

 

Create a new counter:

Create counter

 

>> c1 = Counter.call
=> {:get_x=>#<Proc:0x007fa92904ea28@/counter.rb:4 (lambda)>, :incr=>#<Proc:0x007fa92904e910@/counter.rb:6 (lambda)>, :decr=>#<Proc:0x007fa92904e898@/counter.rb:8 (lambda)>}
Counter c1 is a hash where each key points to a Proc. Let’s perform some operations on the counter:
>> c1[:incr].call => 1
>> c1[:incr].call => 2
>> c1[:incr].call => 3
>> c1[:decr].call => 2
>> c1[:get_x].call => 2
Let’s create another counter, c2. Is c2 distinct from c1? In other words, do they behave like distinct objects?
>> c2 = Counter.call
=> {:get_x=>#<Proc:0x007fa92a1fcc98@/counter.rb:4 (lambda)>, :incr=>#<Proc:0x007fa92a1fcc70@/counter.rb:6 (lambda)>, :decr=>#<Proc:0x007fa92a1fcc48@/counter.rb:8 (lambda)>}
>> c2[:get_x].call => 0
>> c1[:get_x].call => 2

Both c1 and c2 get their own x. So there you have it: it is entirely possible to have objects without classes. In fact, this technique is often used in JavaScript to make sure that variables do not leak out and inadvertently become overridden by some other function or operation.

 

While you probably wouldn’t want to use this technique in your day-to-day Ruby programming, there’s an important lesson to be drawn from here. The scoping rules of Ruby are such that when lambda is defined, that lambda also has access to all the variables that are in scope.

 

As the counterexample has shown, closures restrict access to the variables they wrap. 

 

If you have done any amount of JavaScript programming, you would most definitely have encountered the use of callbacks. When used judiciously, callbacks are a very powerful technique. The next section shows how you can achieve the same benefits of callbacks in Ruby.

 

Implementing Callbacks in Ruby with Lambdas

Ruby with Lambdas

 

The report generator is straightforward:

require 'ostruct'
class Generator
attr_reader :report
def initialize(report)
@report = report
end
def run
report.to_csv
end
end

The Generator takes in a report. The Generator#run delegates the call to the report’s to_csv method.

For simplicity’s sake, let’s use the OpenStruct class. This is an example of a report without any error:

good_report = OpenStruct.new(to_csv: "59.99,Great Success")

An erroneous report is represented by a nil value in to_csv like so:

bad_report = OpenStruct.new(to_csv: nil)

 

Let’s take a step back now and try to sketch out how to implement this. Recall that we need to handle two cases. The first case is when things are rosy and the report is sent to the boss; the other is when things go horribly wrong and we want to know about it.

Here’s how this might look:

Notifier.new(Generator.new(good_report),
on_success: lambda { |r| puts "Send #{r} to boss@acme.co" },
on_failure: lambda { puts "Send email to ben@acme.co"} ).tap do |n|
n.run
end

A Notifier takes a Generator object and a Hash that represents the callbacks for the success and failure cases, respectively. Finally, the run method is called to invoke the notifier.

 

If you’re looking at the previous code listing and thinking to yourself, “Wait a minute, this looks almost like functional programming!” give yourself a pat on the back. Although Ruby is an object-oriented language, supporting features such as lambdas blur the lines between object-oriented and functional programming.

 

In particular, the feature that you might be thinking about is passing around functions as first-class values. Understanding what this means is useful especially when you go further into functional programming. So let’s take a short detour and investigate.

 

First-Class Values

Class Values

Think about the way you use an integer or a string. You can assign either to a variable. You can also pass them into methods. Finally, integers and strings can be return values of methods. These characteristics make them values.

 

What about lambdas? Are they values? In order to answer that question, they need to fulfill the same three prerequisites as integers and strings.

Can a lambda be assigned to a variable? Fire up IRB and find out:

>> is_even = lambda { |x| x % 2 == 0 }
=> #<Proc:0x007fa8a309c448@(IRB):1 (lambda)>
>> is_even.call(4)
=> true
>> is_even.call(5)
=> false

 

Check. Next, can a lambda be passed into a method? Define complement. This method takes a predicate lambda and a value, and returns the negated result:

>> def complement(predicate, value)
>> not predicate.call(value)
>> end
=> :complement
>> complement(is_even, 4)
=> false
>> complement(is_even, 5)
=> true

 

So yes, a lambda can most definitely be passed into a lambda. Now, for the final hurdle: can a lambda be a return value? Modify complement so that it only takes in one argument:

>> def complement(predicate)
>> lambda do |value|
>> not predicate.call(value)
>> end
>> end
=> :complement

 

What would you expect now if you invoked complement(is_even)? Let’s find out:

>> complement(is_even)
=> #<Proc:0x007fa8a31ef4f8@(IRB):8 (lambda)>
We get back another lambda: Strike three! For completeness, go ahead and supply some values:
>> complement(is_even).call(4)
=> false
>> complement(is_even).call(5)
=> true

As you can see, we have been treating lambdas as first-class functions all along. Being able to pass around functions like values means that we can conveniently assign, pass around, and return tiny bits of computation.

 

It should be noted that Ruby’s methods are not first-class functions. Instead, lambdas, Procs, and blocks step in to fill in that role.We will circle back now to implementing Notifier and see how first-class functions can lead to decoupled code.

 

Implementing Notifier

Let’s see how Notifier can be implemented:
closures/notifier.rb
class Notifier
attr_reader :generator, :callbacks
def initialize(generator, callbacks)
@generator = generator
@callbacks = callbacks
end
def run
result = generator.run
if result
callbacks.fetch(:on_success).call(result)
else
callbacks.fetch(:on_failure).call
end
end
end

The meat of the code lies in the run method. the result contains the generated report. If the result is non-nil, then the on_success callback is invoked. Otherwise, the on_failure one will be called.

 

There’s a tiny subtlety that is easy to miss, and that’s where the beauty of this technique lies.

Take a closer look at how the on_success callback is defined: on_success: lambda { |r| puts "Send #{r} to boss@acme.co" }

 

What’s the value of r? Well, at the point where this lambda was defined, no one knows. It is only at the point when we know that the generated report is non-nil do we pass the result into the success callback.

 

Let’s work with some examples. First, create a good report and run the notifier:

good_report = OpenStruct.new(to_csv: "59.99,Great Success")
Notifier.new(Generator.new(good_report),
on_success: lambda { |r| puts "Send #{r} to boss@acme.co" },
on_failure: lambda { puts "Send email to ben@acme.co"} ).tap do |n|
n.run #=> Send 59.99,Great Success to boss@acme.co end
Now, create a bad report and run the notifier again:
bad_report = OpenStruct.new(to_csv: nil)
Notifier.new(Generator.new(bad_report),
on_success: lambda { |r| puts "Report sent to boss@acme.co: #{r}" },
on_failure: lambda { puts "Whoops! Send email to ben@acme.co"} ).tap do |n|
n.run #=> Whoops! Send email to ben@acme.co end

This is a very flexible technique. Notice that Notifier doesn’t dictate how you should handle success and failure cases. All it does is invoke the appropriate callbacks.

 

This means that you are free to log errors to a file or send your boss an SMS when a report has been successfully generated—all without modifying the original Notifier class.

 

Now you should be ready for something slightly more challenging. You will get to implement one of the most useful operations inspired by functional programming—fold left.

 

Implementing Enumerable#reduce (or Fold Left)

class functions

 

Multiplying Values Using reduce

Let’s implement something similar, but for the multiply operation. Here’s how we’d invoke it:
multiplier.call(2, [2, 4, 6])) #=> 96
Can you figure out what the implementation of multiplier would look like? It turns out that it looks quite similar to adder:
closures/multiplier.rb
multiplier = lambda do |acc, arr|
if arr.empty?
acc
else
multiplier.call(acc * arr.first, arr.drop(1))
end
end

What has changed? Other than the variable names, the only thing that changed is the binary operation represented by the * multiplication symbol. Your “Don’t Repeat Yourself” alarm should be going off right about now because there’s a lot of duplication between adder and multiplier. Let’s put our refactoring hat on and get cracking!

 

Abstracting a Reducer

Reducer

As it stands, each binary operation is hard-coded into the branch when the array is not empty. What we can do here is to pull out the binary operation, make it an argument, and pass it into the recursive call of the lambda.

 

Here’s a possible first attempt. The reducer lambda takes an additional parameter —binary_function, a lambda that represents the binary operation. Try this out and you’ll realize that it works as expected:

reducer = lambda do |acc, arr, binary_function| if arr.empty?
acc
else
reducer.call(binary_function.call(acc, arr.first), arr.drop(1), binary_function)
end
end
reducer.call(1, [1,2,3,4,5], lambda { |x, y| x + y }) #=> 16

What do you think of this piece of code? I don’t like that binary_function is being passed as the third argument in the recursive call. In other words, since we are already passing in binary_function when reducer is invoked, we shouldn’t have to explicitly pass it in at every other invocation.

 

We can achieve this by abstracting a lambda that takes just the accumulator and the array, as seen on line 2. Here’s the result:

closures/reducer.rb
1: reducer = lambda do |acc, arr, binary_function|
-reducer_aux = lambda do |acc, arr|
-if arr.empty?
- acc
5: else
- reducer_aux.call(binary_function.call(acc, arr.first), arr.drop(1))
- end
- end
-
10: reducer_aux.call(acc, arr)
- end
-
- reducer.call(1, [1,2,3,4,5], lambda { |x, y| x + y }) #=> 16

 

binary_function is a free variable inside of reducer_aux. The value of binary_function is supplied by the reducer, the outermost lambda.

So there you have it. You should now have a better idea of how Enumerable#reduce is implemented behind the scenes. reduce is a very useful tool to have, and is used in most functional languages to great effect. This implementation using lambdas is very similar to how functional languages might implement it.

 

Having lambdas as a language construct enables a very different kind of programming style that you might not otherwise be used to—a testament to the versatility of Ruby.

 

Test Your Understanding!

Now it’s time to work that gray matter! Do the following exercises to be sure you have grasped the concepts that were presented in this blog. Don’t skip this!

  • 1. What is the definition of a closure?
  • 2. Identify the free variable in the following:
 style="margin:0;height:198px;width:971px">def is_larger_than(amount)
lambda do |a|
a > amount
end
end
Here’s an example run:
>> larger_than_5 = is_larger_than(5)
>> larger_than_5.call(4)
=> false
>> larger_than_5.call(5)
=> false
>> larger_than_5.call(6)
=> true

 

3. You work in a music store and you’ve been tasked with writing a miniature database to store artists and album titles. The database should be able to insert, delete, and list entries, but you cannot use objects other than arrays and hashes. Only lambdas are allowed. Here’s the API:

>> db = new_db.call
>> db[:insert].call("Eagles", "Hell Freezes Over")
=> Hell Freezes Over
>> db[:insert].call("Pink Floyd", "The Wall")
=> The Wall
>> db[:dump].call
=> {"Eagles"=>"Hell Freezes Over", "Pink Floyd"=>"The Wall"}
>> db[:delete].call("Pink Floyd") => The Wall
>> db[:dump].call
=> {"Eagles"=>"Hell Freezes Over", "Pink Floyd"=>nil}
4. The complement method was previously defined as such:
def complement(predicate)
lambda do |value|
not predicate.call(value)
end
end
Convert complement into a lambda that returns another lambda. You should then be able to invoke complement like so:
>> complement.call(is_even).call(4)
=> false
>> complement.call(is_even).call(5)
=> true

5. Usually, we think of reducing as combining the elements of a list into a single value. However, you might be surprised to realize that it is more general than that. Here’s a challenge. By only using reduce, take [1, 2, 3, 4, 5] and turn it into [2, 4, 6, 8, 10].

 

Separating the General from the Specific

programming technique

Imagine that you’re working on an e-commerce site that sells sports shoes, and you want to display a selection of the products on the main page:

>> require 'ostruct'
>> catalog = []
>> catalog << OpenStruct.new(name: 'Nike', qty: 20, price: 99.00)>> catalog << OpenStruct.new(name: 'Adidas', qty: 10, price: 109.00)>> catalog << OpenStruct.new(name: 'New Balance', qty: 2, price: 89.00)

 

It’s plain to see that we have a pretty wide selection of footwear. Now, the boss wants to display the products by the lowest priced first:

> catalog.sort_by { |x| x.price }
=> [#<OpenStruct name="New Balance", qty=2, price=89.0>,
#<OpenStruct name="Nike", qty=20, price=99.0>, #<OpenStruct name="Adidas", qty=10, price=109.0>]
What if now she wants the products with the highest quantity to be displayed first?
>> catalog.sort_by { |x| x.qty }.reverse
=> [#<OpenStruct name="Nike", qty=20, price=99.0>, #<OpenStruct name="Adidas", qty=10, price=109.0>, #<OpenStruct name="New Balance", qty=2, price=89.0>]

 

In both instances, all you had to change was the code in the block. In fact, you didn’t have to change the implementation of Enumerable#sort_by. You were able to trust that the method would do its job provided you gave it a reasonable sorting criterion to work with.

So how is this possible? With yield.

 

Executing Blocks with the yield Keyword

When you see yield anywhere in a Ruby block, you should think “execute the block.” Try out the following in IRB:

>> def do_it
>> yield
>> end
=> :do_it
This is a pretty plain-looking piece of code that just executes any block you give it:
>> do_it { puts "I'm doing it" } I'm doing it
=> nil

 

Outputting a string doesn’t return any value. In other words, this block is executed merely for its side effects. Now, let’s make a block that returns a value and pass it into the do_it method:

>> do_it { [1,2,3] << 4 } => [1, 2, 3, 4]

 

What happens when we don’t pass in a block to do_it?

>> do_it

LocalJumpError: no block given (yield)

from (IRB):28:in `do_it'

 

IRB helpfully informs you that the method was not given a block to execute. You might want to pass arguments into a block. For example, say you want a method that passes in two arguments to a block:

 >> def do_it(x, y)
>> yield(x, y)
>> end
=> :do_it
Now, we can pass in and execute any block that takes two arguments:
>> do_it(2, 3) { |x, y| x + y } => 5
>> do_it("Ohai", "Benevolent Dictator") do |greeting, title|
"#{greeting}, #{title}!!!"
end
=> "Ohai, Benevolent Dictator!!!"

There’s a tiny gotcha to yield’s argument-passing behavior. It is more tolerant of missing and extra arguments than you might expect. Missing arguments will be set to nil, and extra arguments will be silently discarded.

 

Let’s modify the method and give it fewer arguments. In this definition of do_it, the yield is only given a single argument:

>> def do_it(x)
>> yield x
>> end
=> :do_it
Observe what happens when the method receives a block that expects two arguments:
>> do_it(42) { |num, line| "#{num}: #{line}" } => "42: "

 

If you find this behavior slightly strange, you can think of yield acting a little like a parallel assignment, in that nils are assigned to missing arguments:

>> a, b = 1 # => 1

>> b # => nil

As previously noted, missing arguments are assigned nil, which explains the lack of an error. What happens if the yield is given more arguments than expected? Once again, think about the parallel assignment analogy:

>> a, b = 1,2,3 => [1, 2, 3]
>> a
=> 1
>> b
=> 2
In this case, 3 is discarded. Redefine do_it once more:
>> def do_it
>> yield "this", "is", "ignored!"
>> end
=> :do_it
Now, pass in a block that takes in no arguments:
>> do_it { puts "Ohai!" } => Ohai!

 

Once again, Ruby executes the code without a hitch. This is also consistent with the parallel assignment behavior as previously demonstrated.

 

Keep in mind this argument passing behavior; otherwise, you might waste precious time figuring out why Ruby doesn’t throw an exception when you think it would, especially when writing unit tests. Now let’s look at the relationship between blocks and closures.

 

Blocks as Closures and Block Local Variables

Blocks Closures

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 a line. That’s because the 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.

 

Enumeration

 

Implementing Fixnum#times

While it’s not surprising that Ruby is an object-oriented language, the extent of “object-orientedness” often surprises newcomers to Ruby. For example, most wouldn’t associate a number with the notion of an object. However, Ruby begs to differ by making code like this possible:

>> 3.times { puts "D'oh!" } D'oh!

D'oh!

D'oh! => 3

How is this possible? The answer is two-fold. First, 3 is an object of the Fixnum class. Second, the Fixnum#times method is what makes the preceding code possible.

 

What can we say about the Fixnum#times method? Well, it executes the block exactly three times. This information is taken from the instance of the Fixnum, 3. This detail is important, as you will soon see.

What can we say about the parameters of the block? Well, not much, since the block doesn’t take any parameters. Let’s implement Fixnum#times. Additionally, we will assume that each doesn’t exist.

 

Create a file called fixnum_times.rb. Fill in an initial implementation like so:

class Fixnum

def times

puts "This does nothing yet!"

end

end

 

Thanks to Ruby’s open classes, we have now just overridden the default version of Fixnum#times and replaced it with our own (currently non-working) one. Load the file in IRB using the following command:

$ IRB -r ./fixnum_times.rb
Let’s try this out:
>> 3.times { puts "D'oh!" } puts "This does nothing yet!"
=> nil

 

For now, nothing happens since we have overridden the default Fixnum#times method with our empty implementation. Remember that we imposed the constraint that we cannot use Array#each? The reason is that would make things too easy for us. We can fall back to a while loop:

blocks/fixnum_times.rb
class Fixnum
def times
x = 0
while x < self
x += 1
yield
end
self
end
end
Now, redo the steps with the updated code:
% IRB -r ./fixnum_times.rb >> 3.times { puts "D'oh!" } D'oh!
D'oh!
D'oh! => 3

 

Again, the self is the Fixnum instance, also known as 3 in our example. In other words, it is using the value of the number to perform the same number of iterations. Pretty nifty, if you ask me. The most important part of the code here is yield.

 

In this example, the yield is called without any arguments, which is exactly what the original implementation expects. The return value of the times method is the number itself, hence self is returned at the end of the method.

 

Let’s keep up the momentum and implement Array#each.
Implementing Array#each
Take a close look at how the Array#each method is invoked:
>> %w(look ma no for loops).each do |x|
>> puts x
>> end
look
ma
no
for
loops
=> ["look", "ma", "no", "for", "loops"]

 

The block accepts one argument. Create a new file, array_each.rb. As with the previous example, fill it with an empty implementation of the method that we are going to override:

class Array
def each
end
end
%w(look ma no for loops).each do |x|
puts x
end
Let’s test that this implementation does override the default behavior:
$ ruby array_each.rb
# Returns nothing

 

Since you don’t have the help of Array#each (that’s the whole point of this exercise), the iteration needs to be tracked manually. Once again, it’s time to reach for the humble while loop:

blocks/array_each.rb
class Array
def each
x = 0
while x < self.length
yield self[x]
x += 1
end
end
end
%w(look ma no for loops).each do |x|
puts x
end
Now when you run array_each.rb, the results get printed as expected:
$ ruby array_each.rb look
ma no for loops

Notice how the self is being used here. First, the while loop uses self.length to determine if it should continue looping or break out of the loop. Second, the individual elements of the array are accessed via self[x]. The value of this is passed into the supplied block, which, in our example, simply prints the elements out. 

 

Blocks can do more than enumeration. In fact, one common use case of blocks is to handle resource management. Let’s explore how.

 

[Note: You can free download the complete Office 365 and Office 2019 com setup Guide.]

 

Implementing File.open

Ruby documentation

Let’s unravel the mysteries of File.open. First of all, the Ruby documentation provides an excellent overview of File.open. If you read carefully, it even provides hints of how it is implemented: With no associated block, File.open is a synonym for:: new.

 

This description alone is enough to kickstart our File. open implementation. Create file_open.rb and follow along.

 

1. If no block is given, File.open is the same as File.new:

class File
def self.open(name, mode)
new(name, mode) unless block_given?
end
end

 

2. If there’s a block, the block is then passed the opened file as an argument...

class File
def self.open(name, mode)
file = new(name, mode)
return file unless block_given?
» yield(file) end
end

 

3. ...and the file is automatically closed when the block terminates...

class File
def self.open(name, mode)
file = new(name, mode)
return file unless block_given?
yield(file)
» file.close
end
end

There’s a gotcha here. What happens if an exception is raised in the block? file.close will not be called, which defeats the whole point of this exercise. Thankfully, this is an easy fix with the ensure keyword:

blocks/file_open.rb
class File
def self.open(name, mode)
file = new(name, mode)
return file unless block_given?
yield(file)
» ensure
» file.close
» end
end

 

Now, file.close is always guaranteed to close properly.

 

4. The value of the block will be returned from File.open.

File.open.
Let’s see if this works. Open file_open.rb in IRB:
% IRB -r ./file_open.rb
Let’s get meta and open the file you just opened:
>> File.open("file_open.rb", "r") do |f|
>> puts f.path
>> puts f.ctime
>> puts f.size
>> end
file_open.rb
2016-11-13 08:32:24 +0800
238
=> nil

 

Since yield(file) is the last line, the value of the block will be returned from With a little bit of work, File.open frees you from having to remember to close file handles, handles exceptional cases, and to top it off, lets you do this in a simple and beautiful API. Speaking of beautiful, blocks are also great for object initialization, as you will soon see in the next section.

 

Implementing an Object Initialization DSL

 Object Initialization DSL

Create a file called object_init.rb. You will need to set up the modules and class

for Twitter::REST::Client:
blocks/object_init.rb
module Twitter
module REST
class Client
end
end
end
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end

 

If you try to run this code, you won’t get any errors. Why? Because Ruby ignores the block when it’s not called within the method body since there is no yield (yet). Let’s make that block do something. We know from looking at it that:

  1. It is being called from the initializer.
  2. It accepts one single argument, the config object.
  3. The config object has a couple of setters, such as consumer_key.

 

The main thing to realize is that the config object can be the same instance created by Twitter::REST:: Client.new. Why can?

 

The short answer is that you can make things a bit more complicated by passing in a configuration object, but let’s stick to simple. Therefore, you now know one additional thing:

 

4. config and the instantiated object can be the same thing. Now you should have a better idea:

blocks/object_init.rb
module Twitter
module REST
class Client
» attr_accessor :consumer_key, :consumer_secret,
» :access_token, :access_token_secret
»
» def initialize
» yield self
» end
end
end
end
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end

 

In the initializer, self (that is, the Twitter::REST:: Client instance) is passed into the block. Within the block body, the instance methods are called. These instance methods were created with attr_accessor. You can now try it out:

client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end
p client.consumer_key
What happens if you initialize the client without a block? You’ll get a
LocalJumpError complaining that no block is given:
'initialize': no block given (yield) (LocalJumpError)
This is an easy fix. Remember block_given?? You can use it in initialize:
def initialize
yield self if block_given?
end

Run the code again, and this time everything should work as expected. We will revisit this example soon, where we consider an even more flexible approach to initializing the config object. But before that, let’s enter into the world of meta-programming and DSL creation.

 

Implementing a Router DSL

Router DSL

In the previous section, you saw one flavor of object initialization using blocks. Here’s another example, adapted and modified from Rails. Let’s imagine that you want to define a bunch of routes in a web framework, such as Rails.

 

Routes are rules that you declare for an incoming web request. These rules invoke the appropriate controller and controller method, depending on the pattern of the URL of the incoming web request.

 

For example, if the web server receives a request with http://localhost:3000/users, a route would parse the incoming request URL and ensure that the index method of the UsersController will be invoked.

 

In Rails, this file is located in config/routes.rb. In older versions of Rails, the syntax looked like this:

routes = Router.new do |r|
r.match '/about' => 'home#about'
r.match '/users' => 'users#index'
end
However, as Rails evolved, the way routes were defined also changed:
routes = Router.new do
match '/about' => 'home#about'
match '/users' => 'users#index'
end

In the new syntax, the block no longer expects an argument. With nothing to pass into yield, which object does match belong to? Learning the techniques in creating this variation to object instantiation will also allow you to create DSLs.

 

Create a file called router.RB, and fill it in with this initial implementation:

routes = Router.new do
match '/about' => 'home#about'
match '/users' => 'users#index'
end
class Router
# We are going to implement this!
end
The end goal is to print out the routes:
{"/about"=>"home#about"}
{"/users"=>"users#index"}

An actual router implementation will parse the string and invoke the appropriate controller and method. For our purpose, printing out the routes will suffice. Your job is to fill in the body of the Router class. One way the task can potentially be simplified is to create an implementation that looks like this:

routes = Router.new do |r|

r.match '/about' => 'home#about'

r.match '/users' => 'users#index'

end

 

This should look familiar to you. Let’s go through building up this class, though this time the pace will be faster.

 

The initializer of Router takes a block that accepts a single argument. That argument is the object itself. You should also be able to infer that the Router has an instance method called a match that takes a hash as an argument. Here’s the fastest way to implement this class:

class Router
def initialize
yield self
end
def match(route)
puts route
end
end
Of course, the match method doesn’t do anything interesting. But that’s not the point. Here’s the challenge: how do you get from
routes = Router.new do |r|
r.match '/about' => 'home#about'
r.match '/users' => 'users#index'
end
to this?
routes = Router.new do
match '/about' => 'home#about'
match '/users' => 'users#index'
end

To make that leap, you will need to know about instance_eval and some meta-programming gymnastics. Let’s get to that right away.

 

Changing Context with instance_eval

You need to somehow convince Ruby that the self in self.match '/about' => 'home#about'

refers to the Router instance, not the main object. This is exactly what instance_eval is for. instance_eval evaluates code in the context of the instance. In other words, instance_eval changes self to point to the instance you tell it to.

 

Back to router.rb. Modify initialize to look like this:

class Router
def initialize(&block)
# self is the Router instance instance_eval &block
end
# ...
end

There are some new things with this code. Previously, all our encounters with block invocation were implicit via yield. Here, the code makes explicit that the block should be captured in &block.

 

The reason it needs to be stored in a variable is that the block needs to be passed into instance_eval. What about the "&" in &block? For that, we need to take a quick detour to learn about block-to-Proc conversion.

 

Block-to-Proc Conversion

Block Conversion

We have not covered Procs yet. For now, think of them as lambdas. Blocks are not represented as objects in Ruby. However, instance_eval expects to be given an object. Therefore, we need to somehow turn a block into an object.

 

In Ruby, this is done via a special syntax: &block. When Ruby sees this, it internally converts the captured block into a Proc object. The rules for block-to-Proc conversion can be utterly confusing. Here’s a useful way to remember it:

  • 1. Block → Proc if &block is in a method argument.
  • 2. Proc → Block if &block is in the method body.

 

Now that you understand block-to-Proc conversion, let’s circle back to where we left off and see how everything is put together. This is the final result:

 

blocks/router.rb
class Router
def initialize(&block)
instance_eval &block
end
def match(route)
puts route
end
end
routes = Router.new do
match '/about' => 'home#about'
end

When the Router instance is given a block, it is converted into a Proc, then passed into instance_eval. Since the context where instance_eval is invoked is Router, the Proc object is also evaluated in the Router context. This means that the match method is invoked on the Router instance.

 

Now that you know how instance_eval and block-to-Proc conversion works, let’s revisit an earlier example on object initialization and apply your newfound knowledge.

 

Object Initialization, Revisited

Object Initialization

Let’s say you want the initializer to be slightly more flexible and take an options hash. Furthermore, you also think that specifying config.consumer_secret is too verbose. In other words, something like this:

client = Twitter::REST::Client.new({consumer_key: "YOUR_CONSUMER_KEY"}) do
consumer_secret = "YOUR_CONSUMER_SECRET"
access_token = "YOUR_ACCESS_TOKEN"
access_token_secret = "YOUR_ACCESS_SECRET"
end

 

How could this be implemented? First, the hash of options would need to be iterated through. This would then be followed by calling the block:

» def initialize(options = {}, &block)
» options.each { |k,v| send("#{k}=", v) }
» instance_eval(&block) if block_given?
» end

 

Take note of the extra = in calling send. Because the methods were constructed using attr_accessor, Ruby uses the trailing = to indicate that the method is an attribute writer. Otherwise, it assumes it is an attribute reader.

 blocks/object_init_revised.rb
module Twitter
module REST
class Client
attr_accessor :consumer_key, :consumer_secret, :access_token, :access_token_secret
» def initialize(options = {}, &block)
» options.each { |k,v| send("#{k}=", v) }
» instance_eval(&block) if block_given?
» end
end
end
end
client = Twitter::REST::Client.new({consumer_key: "YOUR_CONSUMER_KEY"}) do
consumer_secret = "YOUR_CONSUMER_SECRET"
access_token = "YOUR_ACCESS_TOKEN"
access_token_secret = "YOUR_ACCESS_SECRET"
end
p client.consumer_key # => YOUR_CONSUMER_KEY p client.access_token # => YOUR_ACCESS_TOKEN

 

Now, anyone initializing a Twitter::Rest:: Client can choose to do it via an options hash, use a block, or use a combination of both. Now it’s time to flex those programming muscles and work on the exercises.

 

Test Your Understanding!

Test Your Understanding

Time to put on that thinking cap and flex that gray matter. Remember, in order to really understand the material, you should attempt to complete the following exercises. None of them should take too long, and you have my permission to peek at the solutions if you get stuck.

 

1. Implement Array#map using Array#each:

%w(look ma no for loops).map do |x|
x.upcase
end
This should return ["LOOK", "MA", "NO", "FOR", "LOOPS"].

 

2. Implement String#each_word:

"Nothing lasts forever but cold November Rain".each_word do |x| puts x
end
This should output:
Nothing
lasts
forever
but
cold
November
Rain

3. It’s your turn to implement File.open. Start off with the Ruby documentation. The key here is to understand where to put pre- and post-processing code, where to put yield, and ensure that resources are cleared up.

 

4. Here’s some real-world code adapted from the Ruby Redis library:

module Redis
class Server
# ... more code ...
def run
loop do
session = @server.accept
begin
return if yield(session) == :exit
ensure
session.close
end
end
rescue => ex
$stderr.puts "Error running server: #{ex.message}"
$stderr.puts ex.backtrace
ensure
@server.close
end
# ... more code ...
end
end

Notice the similarities to the File. open example. Does run require a block to be passed in? How is the return result of the block used? How could this code be called?

 

5. Implementing the ActiveRecord DSL

ActiveRecord DSL

Active Record is an object-relational mapper used in Rails, which connects objects, such as Micropost, to a database table. A migration in Active Record is a file that, when executed, makes changes to the database. Here’s an example of migration in ActiveRecord:

ActiveRecord::Schema.define(version: 20130315230445) do
create_table "microposts", force: true do |t| t.string "content"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end

 

You don’t have to know how Active Record works. Your job is to implement the DSL. Here’s the full code, with placeholders for you to implement your solution:

module ActiveRecord
class Schema
def self.define(version, &block)
# *** <code here> ***
end
def self.create_table(table_name, options = {}, &block)
t = Table.new(table_name, options)
# *** <code here> ***
end
end
class Table
def initialize(name, options)
@name = name
@options = options
end
def string(value)
puts "Creating column of type string named #{value}"
end
def integer(value)
puts "Creating column of type integer named #{value}"
end
def datetime(value)
puts "Creating column of type datetime named #{value}"
end
end
end
ActiveRecord::Schema.define(version: 20130315230445) do
create_table "microposts", force: true do |t|
t.string "content"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end

If you’ve done everything right, you will see:

Creating a column of type string named content

Creating a column of type integer named user_id

Creating a column of type DateTime named created_at

Creating a column of type DateTime named updated_at

 

Procs and the Four Ways of Calling Them

Ruby embraces

Unlike the language named after a certain serpent, Ruby embraces TMTOWTDI (pronounced as Tim Toady), or There’s more than one way to do it. The calling of Procs is a wonderful example. In fact, Ruby gives you four different ways:

1. Proc#call(args)
2. .(args)
3. Threequals
4. Lambdas

Fire up IRB. Let’s begin by creating a very simple Proc:

>> p = proc { |x, y| x + y }

=> #<Proc:0x007ffb12907940@(IRB):1>

 

There are two things to notice here. First, the return value tells you that a Proc has been created. Second, Ruby provides a shorthand to create Procs. This is really a method in the Kernel class:

>> p = Kernel.proc { |x, y| x + y } => #<Proc:0x007ffb12907940@(IRB):1>
Of course, since Proc is just like any other class, you can create an instance of it the usual way:
>> p = Proc.new { |x, y| x + y }
=> #<Proc:0x007ffb12907940@(IRB):1>
Now you know how to create a Proc. Time to make it do some work. The first
way is to use Proc#call(args):
>> p = proc { |x,y| x + y }
>> p.call("oh", "ai") => "ohai"
>> p.call(4, 2)
=> 6

In fact, this is my preferred way of invoking Procs because it conveys the intent of invocation much better than the alternatives, which are presented next.

 

Ruby provides a shorthand for the call(args) method: .(args). Therefore, the previous example could have been rewritten as such:

>> p = proc { |x,y| x + y }
>> p.("oh", "ai")
>> p.(4, 2)
Here’s an interesting Ruby tidbit. Turns out, the .() syntax works across any class that implements the call method. For example, here’s a class with only the call method:
class Carly
def call(who)
"call #{who}, maybe"
end
end
c = Carly.new
c.("me") # => "call me, maybe"

 

You should avoid using. () if you can, because this could potentially confuse other people who might not be familiar with the syntax.

 

Ruby has an even quirkier syntax for invoking Procs:

p = proc { |x,y| x + y }
p === ["oh", "ai"]
The === operator is also known as the threequals operator. This operator makes it possible to use a Proc in a case statement. Look at the following code:
even = proc { |x| x % 2 == 0 }
case 11
when even
"number is even"
else
"number is odd"
end
Here, even, when given a number, returns true or false depending on the case statement. For example:
>> even = proc { |x| x % 2 == 0 }
>> even === 11
=> false
>> even === 10 => true
Note that invoking a Proc that expects a single argument this way is incorrect and results in a confusing error message:
>> even = proc { |x| x % 2 == 0 }
>> even === [11]

NoMethodError: undefined method `%' for [11]:Array from (IRB):1:in `block in IRB_binding' Next, let’s look at lambdas and how they relate to Procs.

 

The Difference Between a Lambda and a Proc

 Lambda Proc

A lambda and a proc have two important differences: arity and return semantics.

 

Arity refers to the number of arguments a function takes. While this definition usually applies to functions or methods, it is also applicable to lambdas and procs. Fire up IRB and let’s do some exploring. Create a lambda and a proc:

>> l = lambda { |x, y| puts "x: #{x}, y: #{y}" }
>> p = proc { |x, y| puts "x: #{x}, y: #{y}" }
Then invoke them:
>> l.call("Ohai", "Gentle Reader")
>> p.call("Ohai", "Gentle Reader")
You’ll see the following result from each:
x: Ohai, y: Gentle Reader

 

Now here comes the interesting bit. What happens if we supply one less argument? Here’s Proc’s response:

>> p.call("Ohai")
#=> x: Ohai, y:
The Proc seems perfectly fine having less than expected arguments. What about
lambda?
>> l.call("Ohai")
=> ArgumentError: wrong number of arguments (1 for 2)
Turns out, lambdas get upset if you give them less arguments than expected. Now, let’s try the same exercise but with one extra argument. Again, here’s Proc:
>> p.call("Ohai", "Gentle", "Reader")
#=> x: Ohai, y: Gentle Reader
Here’s the lambda:
>> l.call("Ohai", "Gentle", "Reader")
#=> ArgumentError: wrong number of arguments (3 for 2)

So, the moral of the story is that lambdas, unlike Procs, expect the exact number of arguments to be passed in. For Procs, unassigned arguments are given nil. Extra arguments are silently ignored.

 

Now, let’s look at the other difference: return semantics. A Proc always returns from the context it was created. Let’s unpack this a little and see why this is important. Create a new file called some_class.rb with the following contents:

procs_lambdas/someclass.rb
class SomeClass
def method_that_calls_proc_or_lambda(procy) puts "calling #{proc_or_lambda(procy)} now!" procy.call
puts "#{proc_or_lambda(procy)} gets called!"
end
def proc_or_lambda(proc_like_thing)
proc_like_thing.lambda? ? "Lambda" : "Proc"
end
end

Should I Use lambdas or Procs?

Use lambdas

For the most part, you should be fine using lambdas. That’s because the return semantics of lambdas resemble the intuitive behavior of methods.

 

Sometimes though, you might want to use Procs. One reason might be you need multiple arities to be supported. You’ll learn more in the Symbol#to_proc section where you’ll reimplement a very nifty Ruby trick made possible by Procs.

 

Now let’s review the Ruby method Symbol#to_proc and see how it’s implemented.

 

Reimplementing Symbol #to_proc

Symbol

In order to understand what happens behind the scenes, you’ll create an object and then pass it into the map. If you’re expecting this to fail, you are absolutely right, but that is the whole point. The error messages that Ruby provides will guide you to enlightenment.

>> obj = Object.new

>> [1,2,3].map &obj

 

TypeError: wrong argument type Object (expected Proc)

The error message is telling us exactly what you need to know. It’s saying that obj is, well, an Object and not a Proc. In other words, we must teach the Object class how to turn itself into a Proc. Therefore, the Object class must have a to_proc method that returns a Proc. Let’s do the simplest thing possible:

>> class Object
>> def to_proc
>> proc {}
>> end
>> end
=> :to_proc
>> obj = Object.new
>> [1, 2, 3].map &obj => [nil, nil, nil]

 

When you run this again, you’ll get no errors. But notice that the result is an array of nils. How can each element be accessed and, say, printed out? The Proc needs to accept arguments:

>> class Object
>> def to_proc
>> proc { |x| "Here's #{x}!" }
>> end
>> end
=> :to_proc
>> obj = Object.new
>> [1,2,3].map(&obj)
=> ["Here's 1!", "Here's 2!", "Here's 3!"]
This hints at a possible implementation of Symbol#to_proc. Let’s start with what you know and redefine to_proc in the Symbol class:
>> class Symbol
>> def to_proc
>> proc { |obj| obj }
>> end
>> end
=> :to_proc
You now know that an expression such as
words.map(&:length)
is equivalent to
words.map { |w| w.length }

 

Here, the Symbol instance is the length. The value of the symbol corresponds to the name of the method. You also know how to access each yielded object by making the Proc return value in to_proc take in an argument.

 

For the preceding example, this is what you want to achieve:

class Symbol
def to_proc
proc { |obj| obj.length }
end
end
You can even try this out:
>> class Symbol
>> def to_proc
>> proc { |obj| obj.length }
>> end
>> end
=> :to_proc
>> ["symbol", "cymbals", "sambal"].map(&:obj) => [6, 7, 6]

Unfortunately, this only works on objects that have the length method.

 

How can Symbol #to_proc be made more general?

Stmbol Method

Well, how can the name of the symbol be turned into a method call on the obj? This can be answered in two parts.

First, using Kernel#send, any method can be invoked on an object dynamically as long as the right symbol is supplied. For example:

>> "ohai".send(:length) => 4

 

In other words, send allows you to dynamically invoke methods using a symbol. In this example, length is hard-coded in the Symbol#to_proc method. The next step is to make the method more general, which brings us to the next part of the answer.

 

Instead of hard-coding: length, you can make use of self, which in the case of a Symbol, returns the value of the symbol.

Therefore, you can make use of self, which holds the value of the name of the method such as length, and pass it to the send method to invoke the method on obj. I hereby present you our own implementation of Symbol#to_proc:

procs_lambdas/symbol_to_proc.rb
class Symbol
def to_proc
proc { |obj| obj.send(self) }
end
end
Try it out. Save this code to a file called symbol_to_proc.rb and then load it in IRB:
$ IRB -r ./symbol_to_proc.rb
Then test it out:
>> ["symbols", "cymbals", "sambal"].map(&:length) => [7, 7, 6]
>> ["symbols", "cymbals", "sambal"].map(&:upcase) => ["SYMBOLS", "CYMBALS", "SAMBAL"]
self is the symbol object (:length in our example), which is exactly what #send expects.

 

You can now clearly see that an argument is missing. The lambda currently accepts only one argument, but what it received was two arguments. You need to allow the lambda to take in more arguments:

class Symbol
def to_proc
» lambda { |obj, args| obj.send(self, *args) } end
end
[1, 2, 3].inject(&:+) # => 6

 

Now it works as expected! The splat operator (that’s the * in *args) enables the method to support a variable number of arguments. Before you go about celebrating, there’s one problem. The following code doesn’t work anymore:

words = %w(underwear should be worn on the inside)
words.map &:length # => [9, 6, 2, 4, 2, 3, 6]
You’ll see the following output when you run it:
ArgumentError: wrong number of arguments (1 for 2)
from (IRB):3:in `block in to_proc'
from (IRB):8:in `map'

 

There are two ways to fix this. First, you can supply args with a default value:

procs_lambdas/symbol_to_proc_lambda.rb
class Symbol
def to_proc
» lambda { |obj, args=nil| obj.send(self, *args) }
end
end
words = %w(underwear should be worn on the inside)
words.map &:length # => [9, 6, 2, 4, 2, 3, 6]
[1, 2, 3].inject(&:+) # => 6
Alternatively, you can just use a Proc again:
procs_lambdas/symbol_to_proc_final.rb
class Symbol
def to_proc
» proc { |obj, args| obj.send(self, *args) } end
end
words = %w(underwear should be worn on the inside)
words.map &:length # => [9, 6, 2, 4, 2, 3, 6]
[1, 2, 3].inject(&:+) # => 6

 

This is one of the few places where having a more relaxed requirement with respect to arities is important and even required.

 

Currying with Procs

function

Why Was Proc#curry Added?

Here’s the cheeky response coming straight from Yukihiro Matsumoto, creator of Ruby:

I consider this method (Proc#curry) to be trivial and should be treated like an Easter egg for functional programming kids.

 

Alright, so even Ruby’s creator thinks that you wouldn’t have much use for Proc#curry. But don’t let that dampen your learning spirit! Currying is very useful for creating new functions from existing ones.

 

It gets more useful in functional languages (and languages such as Haskell use it to great effect), but you can see examples of this in Ruby:

 >> greeter = lambda do |greeting, salutation, name|
>> "#{greeting} #{salutation} #{name}"
>> end
In the preceding function, if you wanted to use greeter, you would have to supply all three arguments:
>> greeter.call("Dear", "Mr.", "Gorbachev") => "Dear Mr. Gorbachev"

 

What if you wanted to construct a greeter that always started with “Dear”? With a curried Proc or lambda, you very well can:

 greeter = lambda do |greeting, salutation, name|
>> "#{greeting} #{salutation} #{name}"
>> end
>> dear_greeter = greeter.curry.call("Dear")
=> #<Proc:0x007f902ba542f0 (lambda)>
With dear_greeter defined, you can use it like so:
>> dear_greeter.call("Great").call("Leader") => "Dear Great Leader"
Of course, if you find .call slightly verbose, you can always write it this way:
>> dear_greeter.("Great").("Leader") => "Dear Great Leader"
Here, dear_greeter is constructed from greeter by partially applying the first argument. Let’s see another example:
sum_ints = lambda do |start, stop|
(start..stop).inject { |sum, x| sum + x }
end
sum_of_squares = lambda do |start, stop| (start..stop).inject { |sum, x| sum + x*x }
end
sum_of_cubes = lambda do |start, stop| (start..stop).inject { |sum, x| sum + x*x*x }
end
What do you notice? All three of the preceding lambdas have the same structure:
sum = lambda do |start, stop|
(start..stop).inject { |sum, x| sum + ??? }
end

 

This suggests that some form of refactoring can occur. The only thing that is different is the portion marked as ???. It’s straightforward to extract out the common logic and make it an argument:

sum = lambda do |fun, start, stop|
(start..stop).inject { |sum, x| sum + fun.call(x) }
end
So what does this buy you? Well, now you can make use of sum to build other methods:
sum_of_ints = sum.(lambda { |x| x }, 1, 10)
sum_of_squares = sum.(lambda { |x| x*x }, 1, 10)
sum_of_cubes = sum.(lambda { |x| x*x*x }, 1, 10)

 

Of course, even doing it this way requires you to specify all the arguments up front. What if you wanted to calculate sum_of_squares but wanted to defer supplying the ranges? For example, a user has the option to select the range via a form. Here’s currying to the rescue:

sum_of_squares = sum.curry.(lambda { |x| x*x })
Now, you can make use of sum_of_squares with any valid range you desire:
sum_of_squares.(1).(10) => 385
sum_of_squares.(50).(100) => 295475

 

That’s enough currying for now. It’s time to open that terminal and work that brain with some exercises.

Test Your Understanding!

1. Reimplement Symbol#to_proc. Now that you’ve seen how Symbol#to_proc is implemented, you should have a go at it yourself.

2. You can use #to_proc instantiate classes. . Consider this behavior:
class SpiceGirl
def initialize(name, nick)
@name = name
@nick = nick
end
def inspect
"#{@name} (#{@nick} Spice)"
end
end
spice_girls = [["Mel B", "Scary"], ["Mel C", "Sporty"],
["Emma B", "Baby"], ["Geri H", "Ginger",], ["Vic B", "Posh"]]
p spice_girls.map(&SpiceGirl)
This returns:
[Mel B (Scary Spice), Mel C (Sporty Spice),
Emma B (Baby Spice), Geri H (Ginger Spice), Vic B (Posh Spice)]

 

This example demonstrates how to_proc can be used to initialize a class.

Implement this.

3. How can you tell the difference between a Proc and lambda in Ruby?

4. What is the class of proc {}? What about lambda {}?

5. Which of these will cause an error? Why?

join_1 = proc { |x, y, z| "#{x}, #{y}, #{z}"
join_2 = lambda { |x, y, z| "#{x}, #{y}, #{z}"
}
}
join_1.call("Hello", "World")
join_2.call("Hello", "World")
6. Which of these will cause an error? Why?
join_1 = proc { |x, y, z| x + y + z }
join_2 = lambda { |x, y, z| x + y + z }
join_1.call(1, 2)
join_2.call(1, 2)

 

Building Your Own Lazy Enumerables

Lazy Enumerables

In this blog, you’ll implement your own cheap counterfeit of Ruby’s lazy enumerable using the concepts you’ve learned in the rest of this blog. Going through this process will also expose you to some of the more advanced concepts of blocks, enumerables, and enumerations.

 

So how can you convince Ruby not to evaluate every single value? Enter Enumerable#lazy. This method creates a lazy enumerable. Now, to infinity and

beyond:

>> 1.upto(Float::INFINITY).lazy.map { |x| x * x } => #<Enumerator::Lazy: #<Enumerator::Lazy:

#<Enumerator: 1:upto(Infinity)>>:map>

With lazy, the 1.upto(Float::INFINITY) enumerator has been made lazy by being wrapped up in an Enumerator::Lazy class, which has a lazy version of map.

 

Let’s try the very first expression that caused the infinite computation, but this time with Enumerable#lazy:

>> 1.upto(Float::INFINITY).lazy.map { |x| x * x }.take(10) => #<Enumerator::Lazy: #<Enumerator::Lazy:

#<Enumerator::Lazy:

#<Enumerator: 1:upto(Infinity)>>:map>:take(10)>

What just happened? Instead of getting ten values, it turns out that even Enumerable#take is wrapped up! How can you get your values then?

 

Enumerable#to_a to the rescue:

>> 1.upto(Float::INFINITY).lazy.map { |x| x * x }.take(10).to_a => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Why is Enumerable#take also wrapped up? This lets you do more lazy chaining. It allows you to hold off getting values out of the enumerator up until the point where you really need the values.

 

How does this sorcery work behind the scenes? You are about to find out. You will create your own version of lazy enumerables, albeit a minimalistic version. Along the way, you will also learn interesting aspects of Ruby’s enumerators that you probably wouldn’t have known about. Let’s get started.

 

Implementing Lax

Implementing Lax

 

Implementing Lazy map

Let’s implement method chaining on methods such as map and take. Enumerable:: Lax returns a Lax instance, which means we need to define Lax versions of the map and take. Each invocation of map and take will in turn return yet another Lax instance.

 

Recall how the original implementation “wraps” each method call with a “lazy layer.” You’ll do exactly the same thing here.

 

How would the map method in Lax look? Here’s a start:

lazy_enumerable/skeleton/lax.rb
def map(&block)
end

 

You also know that you would need to return a new Lax instance. Also, since the map is going to be chained, you should expect that the self is going to be another Lax instance:

lazy_enumerable/skeleton/lax.rb
def map(&block)
» Lax.new(self) end

 

For the method’s logic, you’ll want to populate the yielder object with elements that are going to be modified by the map method. That means that you must somehow pass two things into the Lax instance in the map: the yielder object, and the element to be mapped on. You can pass these via a block argument like so:

lazy_enumerable/map/lax.rb
def map(&block)
» Lax.new(self) do |yielder, val|
» yielder << block.call(val)
» end
end

 

What does this do? It looks like the block is invoked with Val, and that element is then passed into the yielder. That’s not completely accurate, though. Instead, it’s more like the instructions of how to treat a mapped value being stored inside the yielder object.

 

Since the Lax initializer accepts a block, the constructor needs to be modified:

lazy_enumerable/map/lax.rb
def initialize(receiver)
super() do |yielder|
receiver.each do |val|
» if block_given?
» yield(yielder, val)
» else
» yielder << val
» end
end
end
end

Let’s trace through what happens when a block is supplied, using the Lax version of the map method.

Recommend