April 18, 2006 at 07:06 Tags Articles , Ruby

Introduction

Ruby provides the programmer with a set of very powerful features borrowed from the domain of functional programming , namely closures high-order functions and first-class functions [1] . These features are implemented in Ruby by means of code blocks, Proc objects and methods (that are also objects) - concepts that are closely related and yet differ in subtle ways. In fact I found myself quite confused about this topic, having a difficulty to understand the difference between blocks, procs and methods and unsure about the best practices of using them. Additionally, having some background in Lisp and years of Perl experience, I was unsure of how the Ruby concepts map to similar idioms from other programming languages, like Lisp's functions and Perl's subroutines. Sifting through hundreds of newsgroup posts, I saw that I'm not the only one with this problem, and in fact quite a lot of "Ruby Nubies" struggle with the same ideas.

Procs

In this article I lay out my understanding of this facet of Ruby, which comes as a result of extensive research of Ruby books, documentation and comp.lang.ruby, in sincere hope that other people will find it useful as well.Shamelessly ripping from the Ruby documentation, Procs are defined as follows:

def gen_times(factor) return Proc.new {|n| n*factor } end times3 = gen_times(3) times5 = gen_times(5) times3.call(12) #=> 36 times5.call(5) #=> 25 times3.call(times5.call(4)) #=> 60

A useful example is also provided:Procs play the role of functions in Ruby. It is more accurate to call them function objects, since like everything in Ruby they are objects. Such objects have a name in the folklore - functors. A functor is defined as, which is exactly what a Proc is.

More on Procs

gen_times

def foo (a, b) a.call(b) end putser = Proc.new {|x| puts x} foo(putser, 34)

lambda

lambda

putser = lambda {|x| puts x}

lambda

Proc.new

lambda

lamb = lambda {|x, y| puts x + y} pnew = Proc.new {|x, y| puts x + y} # works fine, printing 6 pnew.call(2, 4, 11) # throws an ArgumentError lamb.call(2, 4, 11)

return

Proc.new

return

def try_ret_procnew ret = Proc.new { return "Baaam" } ret.call "This is not reached" end # prints "Baaam" puts try_ret_procnew

return

lambda

def try_ret_lambda ret = lambda { return "Baaam" } ret.call "This is printed" end # prints "This is printed" puts try_ret_lambda

lambda

Proc.new

Methods

class Boogy def initialize @dix = 15 end def arbo puts "#{@dix} ha

" end end # initializes an instance of Boogy b = Boogy.new # prints "15 ha" b.arbo

arbo

send

Object

arbo

# method/message name is given as a string b.send("arbo") # method/message name is given as a symbol b.send(:arbo)

def say (something) puts something end say "Hello"

say

say

say = lambda {|something| puts something} say.call("Hello") # same effect say["Hello"]

[]

call

Blocks

# a naked block can't live in Ruby # this is a compilation error ! {puts "hello"} # now it's alive, having been converted # to a Proc ! pr = lambda {puts "hello"} pr.call

Passing a block to a method

10.times do |i| print "#{i} " end numbers = [1, 2, 5, 6, 9, 21] numbers.each do |x| puts "#{x} is " + (x >= 3 ? "many" : "few") end squares = numbers.map {|x| x * x}

do |x| ... end

{ |x| ... }

From the example and the definition above, it is obvious that Ruby Procs can also act as closures. On Wikipedia, a closure is defined as. Note how closely it maps to the Ruby definitionProcs in Ruby are first-class objects , since they can be created during runtime, stored in data structures, passed as arguments to other functions and returned as the value of other functions. Actually, theexample demonstrates all of these criteria, except for "passed as arguments to other functions". This one can be presented as follows:There is also a shorthand notation for creating Procs - the Kernel method [2] (we'll come to methods shortly, but for now assume that a Kernel method is something akin to a global function, which can be called from anywhere in the code). Usingthe Proc object creation from the previous example can be rewritten as:Actually, there are two slight differences betweenand. First, argument checking. The Ruby documentation forstates:. Here is an example to demonstrate this:Second, there is a difference in the way returns are handled from the Proc. Afromreturns from the enclosing method (acting just like afrom a block, more on this later):Whilefromacts more conventionally, returning to its caller:With this in light, I would recommend usinginstead of, unless the behavior of the latter is strictly required. In addition to being way cooler a whopping two characters shorter, its behavior is less surprising.Simply put, a method is also a block of code. However, unlike Procs, methods are not bound to the local variables around them. Rather, they are bound to some object and have access to its instance variables [3] A useful idiom when thinking about methods is. Given a receiver - an object that has some method defined, we can send it a message - by calling the method, optionally providing some arguments. In the example above, callingis akin to sending a message "arbo", without arguments. Ruby supports the message sending idiom more directly, by including themethod in class(which is the parent of all objects in Ruby). So the following two lines are equivalent to themethod call:Note that methods can also be defined in the "top-level" scope, not inside any class. For example:While it seems thatis "free-standing", it is not. When methods such as this are defined, Ruby silently tucks them into the Object class. But this doesn't really matter, and for all practical purposescan be seen as an independent method. Which is, by the way, just what's called a "function" in some languages (like C and Perl). The following Proc is, in many ways similar:Theconstruct is a synonym toin the context of Proc [4] . Methods, however, are more versatile than procs and support a very important feature of Ruby, which I will present right after explaining what blocks are.Blocks are so powerfully related to Procs that it gives many newbies a headache trying to decipher how they actually differ. I will try to ease on comprehension with a (hopefully not too corny) metaphor. Blocks, as I see them, are unborn Procs. Blocks are the larvae, Procs are the insects. A block does not live on its own - it prepares the code for when it will actually become alive, and only when it isand converted to a Proc, it starts living:Is that it, is that what all the fuss is about, then ? No, not at all. The designer of Ruby, Matz saw that while passing Procs to methods (and other Procs) is nice and allows high-level functions and all kinds of fancy functional stuff, there is one common case that stands high above all other cases - passing a single block of code to a method that makes something useful out of it, for example iteration. And as a very talented designer, Matz decided that it is worthwhile to emphasize this special case, and make it both simpler and more efficient.No doubt that any programmer who has spent at least a couple of hours with Ruby has been shown the following examples of Ruby glory (or something very similar):(Note thatis equivalent to

Such code is IMHO part of what makes Ruby the clean, readable and wonderful language it is. What happens here behind the scenes is quite simple, or at least may be depicted in a very simple way. Perhaps Ruby doesn't implement it exactly the way I'm going to describe it, since there are optimization considerations surely playing their role - but it is definitely close enough to the truth to serve as a metaphor for understanding.

yield

def do_twice yield yield end do_twice {puts "Hola"}

do_twice

yield

def do_twice(what) what.call what.call end do_twice lambda {puts "Hola"}

yield

def do_twice(what1, what2, what3) 2.times do what1.call what2.call what3.call end end do_twice( lambda {print "Hola, "}, lambda {print "querido "}, lambda {print "amigo

"})

yield

The ampersand (&)

Whenever a block is appended to a method call, Ruby automatically converts it to a Proc object, but one without an explicit name. The method, however, has a way to access this Proc, by means of thestatement. See the following example for clarification:The methodis defined and called with an attached block. Although the method didn't explicitly ask for the block in its arguments list, thecan call the block. This can be implemented in a more explicit way, using a Proc argument:This is equivalent to the previous example, but using blocks withis cleaner, and better optimized since only one block is passed to the method, for sure. Using the Proc approach, any amount of code blocks can be passed:It is important to note that many people frown at passing blocks, and prefer explicit Procs instead. Their rationale is that a block argument is implicit, and one has to look through the whole code of the method to see if there are any calls tothere, while a Proc is explicit and can be immediately spotted in the argument list. While it's simply a matter of taste, understanding both approaches is vital.The ampersand operator can be used to explicitly convert between blocks and Procs in a couple of esoteric cases. It is worthy to understand how these work.

def contrived(a, &f;) # the block can be accessed through f f.call(a) # but yield also works ! yield(a) end # this works contrived(25) {|x| puts x} # this doesn't (ArgumentError), because &f; # isn't really an argument - it's only there # to convert a block contrived(25, lambda {|x| puts x})

print "(t)imes or (p)lus: " times = gets print "number: " number = Integer(gets) if times =~ /^t/ calc = lambda {|n| n*number } else calc = lambda {|n| n+number } end puts((1..10).collect(&calc;).join(", "))

collect

calc

collect

Remember how I said that although an attached block is converted to a Proc under the hood, it is not acessible as a Proc from inside the method ? Well, if an ampersand is prepended to the last argument in the argument list of a method, the block attached to this method is converted to a Proc object and gets assigned to that last argument:Another (IMHO far more efficacious) use of the ampersand is the other-way conversion - converting a Proc into a block. This is very useful because many of Ruby's great built-ins, and especially the iterators, expect to receive a block as an argument, and sometimes it's much more convenient to pass them a Proc. The following example is taken right from the excellent "Programming Ruby" book by the pragmatic programmers:Themethod expects a block, but in this case it is very convenient to provide it with a Proc, since the Proc is constructed using knowledge gained from the user. The ampersand precedingmakes sure that the Proc object calc is turned into a code block and is passed toas an attached block.

words = %w(Jane, aara, multiko) upcase_words = words.map {|x| x.upcase} p upcase_words

upcase

map

x

p "Erik".send(:upcase)

map {|x| x.upcase}

to_proc

to_proc

Symbol

class Symbol # A generalized conversion of a method name # to a proc that runs this method. # def to_proc lambda {|x, *args| x.send(self, *args)} end end # Viola ! words = %w(Jane, aara, multiko) upcase_words = words.map(&:upcase)

Conclusion

The ampersand also allows the implementation of a very common idiom among Ruby programmers: passing method names into iterators. Assume that I want to convert all words in an Array to upper case. I could do it like this:This is nice, and it works, but I feel it's a little bit too verbose. Themethod itself should be given to, without the need for a separate block and the apparently superfluousargument. Fortunately, as we saw before, Ruby supports the idiom of sending messages to objects, and methods can be referred to by their names, which are implemented as Ruby. For example:This, quite literally, says. This feature can be utilized to implement thein an elegant manner, and we're going to use the ampersand for this ! As I said, when the ampersand is prepended to some Proc in a method call, it converts the Proc to a block. But what if we prepend it not to a Proc, but to another object ? Then, Ruby's implicit type conversion rules kick in, and themethod is called on the object to try and make a Proc out of it. We can use this to implementforand achieve what we want:Ruby doesn't really have functions. Rather, it has two slightly different concepts - methods and Procs (which are, as we have seen, simply what other languages call function objects, or functors). Both are blocks of code - methods are bound to Objects, and Procs are bound to the local variables in scope. Their uses are quite different.

Methods are the cornerstone of object-oriented programming, and since Ruby is a pure-OO language (everything is an object), methods are inherent to the nature of Ruby. Methods are the actions Ruby objects- the messages they receive, if you prefer theidiom.

lambda

lambda

Procs make powerful functional programming paradigms possible, turning code into a first-class object of Ruby allowing to implement high-order functions. They are very close kin to Lisp'sforms (there's little doubt about the origin of Ruby's Proc constructor

yield

The construct of a block may at first be confusing, but it turns out to be quite simple. A block is, as my metaphor goes, an unborn Proc - it is a Proc in an intermediate state, not bound to anything yet. I think that the simplest way to think about blocks in Ruby, without losing any comprehension, would be to think that blocks are really a form of Procs, and not a separate concept. The only time when we have to think of blocks as slighly different from Procs is the special case when they are passed as the last argument to a method which may then access them using

Notes

That's about it, I guess. I know for sure that the research I conducted for this article cleared many misunderstandings I had about the concepts presented here. I hope others will learn from it as well. If you see anything you don't agree with - from glaring errors to nitpicky inaccuracies, feel free to comment - I'll be happy to discuss any remarks and fix my mistakes. [1] It seems that in the pure, theoretical interpretation what Ruby has isn't first-class functions per se. However, as this article demonstrates, Ruby is perfectly capable of fulfilling most of the requirements for first-class functions, namely that functions can be created during the execution of a program, stored in data structures, passed as arguments to other functions, and returned as the values of other functions.

lambda

proc

proc

Proc.new

lambda

has a synonym -, which is considered 'mildly deprecated' (mainly becauseandare slightly different, which is confusing). In other words, just use

[3] These are 'instance methods'. Ruby also supports 'class methods', and 'class variables', but that is not what this article is about.

call

[]