4 Building blocks
4.1 Methods and Functions
Basically, basically, however, Only so only We’ve already had a good deal to say about functions, even “anonymous” functions, which we call lambdas. But what about regular, named functions?
Here’s a definition of one in Ruby
def proclaim_appreciation(person)
"#{person} is positively awesome <3 !"
end
This function has a name, proclaim_appreciation. It takes a single argument, which can be anything that can be converted to a string, and its return value is a String. It’s a pure function: given the same argument it will always return the same value. And it does not cause any side effects, such as writing to a file.
In typical Ruby code, such free standing functions are rare. Most function definitions will be part of a class definition.
class WildAnimal
attr_reader :strength
def initialize(strength)
@strength = strength
end
def roar!
if self.strength < 5
"miauw"
elsif self.strength < 10
"roar"
else
"RAAWWWRRRR"
end
end
end
cat = WildAnimal.new(3)
cat.roar!
# => "miauw"
Functions like roar! in this example are called methods. A method is always called on an object, in this case cat. So if methods are a kind of function, can a method be a pure function? Is roar! a pure function?
At first glance it’s not. It doesn’t even take any arguments, yet it can produce three different results! However, when you look closely you’ll notice that roar! has access to self, which is the object we called the method on, in this case, cat. Looking at it from this angle, you’ll find that for the same value of self, roar! will indeed always return the same result, so it is fact a pure function.
In Ruby everything is an object, even things like numbers or true and false. And all functions you define are actually methods on objects. It is possible to declare methods outside of any classes or modules, but all this does is simply make the method available on all objects. 5
When calling a method it will always implicitly receive an extra self argument, whether the method will use it or not. When writing a function that only acts on its actual, explicit arguments, the question is where to put it, since it’s not tied to a specific object.
The first option would be to simply put it in the top-level scope, like the proclaim_appreciation function above. This is the simplest solution. The downside is that the function will now be added to all objects, which gets a bit ugly after a while.
It would be better if we could somehow isolate the function from the rest of the program, so its name can’t incidentally clash with that of another method. Let’s try with a module
module Exclamations
def proclaim_appreciation(thing)
# ...
end
end
Now when we want to use this function, all we need to do is add include Exclamations to the surrounding class or module. Isolation accomplished!
But can we do better? Maybe we don’t want to mix in the Exclamations module, maybe we’re calling this method only once. We could also declare it on the Module object itself.
(In Ruby everything is an objects, including modules, which are instances of the class Module.)
module Exclamations
def self.proclaim_appreciation(person)
# ...
end
end
Great! Now we can call the function with Exclamations.proclaim_appreciation(my_best_friend), and we are no longer forced to include the function in our classes where it doesn’t really belong.
If you’re having a hard time choosing between these two options, don’t worry, there’s a way to postpone the decision. You could make your module support both ways of calling the function. This is what module_function achieves:
module Exclamations
def proclaim_appreciation(person)
# ...
end
module_function :proclaim_appreciation
end
class Quux
# Style with include
include Exclamation
def quip(person)
proclaim_appreciation
end
end
# Style with direct call on module object
Exclamation.proclaim_appreciation('Ella Fitzgerald')
In Ruby 2.1, which is still in the development at the time of writing, method definitions will return their name as a symbol, so you could abbreviate the definition to
module Exclamations
module_function def proclaim_appreciation(person)
# ...
end
end
Finally, if typing module_function all the time is getting tedious, you can achieve the same effect for all functions in the module at once by having it extend itself
module Exclamations
extend self
def ...
end
end
4.2 Iterators, Enumerables and Enumerators
4.3 Lambdas / procs / blocks
A first requirement for a language to be considered functional is that functions are “first class citizens”. This means that functions themselves stand on the same footing as numbers, text strings or arrays. They can be created at run-time, assigned to variables and passed in or returned by functions.
In Ruby we have a lot of options available for creating functions, for example
lambda {|x| ... }
->(x) { ... }
proc {|x| ... }
Proc.new {|x| ... }
def foo(&block)
# ...
end
foo { # ..block contents .. }
Let’s go over these one by one
Blocks
If you came to Ruby from another programming language, chances are your first “wow”-moment looked something like this:
['train', 'tram', 'bus'].each {|transport|
puts "You could take the #{transport}."
}
Compare that to the Java version:
String[] transports = {"train", "tram", "bus"};
for(int index = 0 ; index < transports.length ; index++) {
system.out.println("You could take the " + transports[index]);
}
In both cases we loop over the items in an array, but Java makes us do a lot more work. We need to use a loop, keep track of an index variable, and pull the items out of the array one by one.
In the Ruby case we simply pass the code that needs to be executed to Array#each. The way we do that is with a block, a kind of anonymous function that we can pass in when calling a method.
To define a function that takes a block, we give it an extra argument, preceded by an ampersand.
def with_square(normal_argument, &block_argument)
block_argument.call(normal_argument * normal_argument)
end
with_square(4) {|sq| puts "The square of 4 is #{sq}" }
In this example we create an anonymous function. It takes a single argument sq, and prints out a message.
We pass this function to with_square, which receives it in block_argument. with_square also takes a regular argument (in this case we pass in the number 4).
Now it’s up to with_square to do as it pleases with these two ingredients. In this case it takes the square of the regular argument, and calls the anonymous function with the result. It could have called it many times, or zero times.
def three_times(&block)
block.call()
block.call()
block.call()
end
three_times { puts "Undulating" }
It’s interesting to know that Ruby also has some shorthand syntax. Rather than capturing the block in an argument variable as we did above, it’s possible to call the block with yield, so the example can be rewritten as.
def three_times(&block)
yield
yield
yield
end
And in fact we can omit the &block variable in this case.
Procs
Playing with these anonymous functions is all fine and dandy, but Ruby is still an object oriented language at heart. Everything is an object, and every object has a class. So what exactly is a block? We can use irb to find out
def what_is_a_block?(&block)
p block
p block.class
end
what_is_a_block? { } # empty block
#=> #<Proc:0x000000036c81f0@(irb):7>
#=> Proc
So these blocks are actually of type Proc, short for “procedure”. We have just discovered our first type of anonymous functions in Ruby!
In idiomatic Ruby code one will rarely see explicit loops like for, while or until. Ruby’s block construct makes them practically unnecessary. In Ruby, when iterating over a collection, the actual iteration happens on the inside, in the internals of the each method. We call these internal iterators, as opposed to the external iterators found in Java and other languages.
In the each loop above something is still keeping track of our position in the array. There is likely still an index variable just like in the Java version. The difference is that we don’t see it, it’s hidden inside the implementation of each.
Now that we know how to “capture” a block into a variable, we can call it, store it in a variable, pass it around to other functions, just like any other Ruby object. But what if we simply want to create one of these Proc objects, these anonymous functions, without jumping through such hoops? Turns out it’s really easy, as with other Ruby classes we can simply call new to create an instance.
(From here on I will also use the shorthand for call: block.() instead of block.call.)
add_one = Proc.new {|x| x + 1 }
# => #<Proc:0x00000002ac6e38@(irb):1>
add_one.(3) # => 4
Or if that’s still too much typing, you can achieve the same thing with the proc method.
sub_one = proc {|x| x - 1 }
Lambdas
In Ruby there are two types of anonymous functions. The first one we’ve already met, the humble proc. But proc has a sister called lambda. Even a far-sighted drunk can see they are related, but it takes a keen eye to spot their differences.
Here’s how to create a lambda
fn = lambda {|x| (["Undulating"] * x).join(', ') }
# => #<Proc:0x000000036cfa90@(irb):8 (lambda)>
fn.(3) #=> "Undulating, Undulating, Undulating"
Note the class of the function object, it’s still a Proc, but it says (lambda) at the end. In many ways lambdas are just procs, but they’re a little bit special.
Ruby 1.9 also introduced an elegant shorthand form, called the “stabby lambda”. It’s the form I will prefer for the rest of the book.
fn = ->(x) { (["Mesmerizing"] * x).join(', ') }
# => #<Proc:0x000000036eab60@(irb):11 (lambda)>
fn.(3) #=> "Mesmerizing, Mesmerizing, Mesmerizing"
Stabby lambdas can also be used with do … end instead of curly braces, which is preferred when the function body spans multiple lines.
poetry_generator = ->(x) do
nouns = %w[banana perfume tree]
adverbs = %w[slowly profoundly lazily merely]
verbs = %w[skyrockets sinks drifts undulates replenishes]
['The', nouns[x%3], adverbs[x%4], verbs[x%5]].join(' ')
end
10.times do |i|
puts poetry_generator.(i)
end
Just like with regular methods, the last expression in the function body is also the functions return value.
-> { 1 ; 2 ; 3}.() # => 3
Differences between lambdas and procs
Ruby had procs long before it had lambdas. Ruby’s creator Yukihiro Matsumoto was inspired by Smalltalk, which also allows passing of code blocks to a method. He found this a very elegant way of generalizing loop constructs, and brought the idea over to Ruby. The land was at peace, the cartoon foxes were happy.
Because blocks were intended to replace things like for loops, it was decided that a return statement inside a block would cause the outer method to return, rather than just the block/proc. For example
def find_tree(collection)
collection.each do |item|
if item =~ /birch|oak|chestnut/
return item
end
end
end
It seems reasonable in this case that the return halfway the method causes the find_tree method to return. However when we think of procs as self-contained functions, then we would expect return to work as in other methods/functions.
factorial = proc do |i|
return 1 if [0,1].include? i
factorial.(i-1) * i
end
factorial.(3)
# LocalJumpError: unexpected return
# from (irb):35:in `block in irb_binding'
# from (irb):35:in `call'
To explicitly return from a proc, we need to use next
factorial = proc do |i|
next 1 if [0,1].include? i
factorial.(i-1) * i
end
factorial.(5) #=> 120
Again this makes more sense in the context of looping over a collection, since in that case returning early from the block causes the loop to skip to the next element.
Suppose we want to process a list of values, but some might be nil, in which case we simply want to skip those
def process_list(list)
list.each do |item|
next if item.nil?
process(item)
end
end
Using return inside a lambda will return from the lambda itself, so we can rewrite the failing factorial example as follows:
factorial = lambda do |i|
return 1 if [0,1].include? i
factorial.(i-1) * i
end
factorial.(3) #=> 6
The other key difference is that a lambda checks its number of arguments, whereas a proc will not complain if you pass in too little, or too many arguments. Any unused arguments will be initialized to nil.
proc {|x,y,z| p [x,y,z] }.(7)
# => [7, nil, nil]
lambda {|x,y,z| p [x,y,z] }.(5)
# ArgumentError: wrong number of arguments (1 for 3)
# from (irb):55:in `block in irb_binding'