2 Introduction

Ruby is designed to make programmers happy. Its creators have examined existing languages carefully, and picked syntax and features to their taste to create a language with a unique flavor. The aroma that dominates the final dish is clearly that of object orientation, owing in no small part to Smalltalk. But Ruby has also been influenced by functional languages, notably LISP. In recent years interest in the functional way of programming has increased, and so a book that covers the intersection between “Ruby” and “Functional” seems long overdue.

People looking to find out what the functional craze is all about might find that much of the existing materials are littered with jargon that is, quite frankly, baffling. Hopefully this book can avoid that trap.

This book has a dual purpose. It is foremost an introduction to the concepts of functional programming, written for Rubyists. It will assume you are reasonably familiar with Ruby, but assumes little else. Beyond that it will be an experiment in seeing how functional Ruby is, or can be. We’ll see that certain parts of idiomatic Ruby already have a functional flavor, and will look at patterns and ideas from the functional side that can enhance your day-to-day programming. We’ll also push the envelope and go a little crazy from time to time. You might not want to use everything you find here in your day job! But hopefully it will be fun and inspiring.

There are good reasons that interest in FP is rising. Functional code has a certain elegance, a purity that can be very appealing. It prevents you from making certain types of mistakes. It enables you to solve certain problems in a very concise way. It is also an exciting time for FP, with novel languages like Clojure, Elixir, Scala or F# getting more followers by the day.

But there is another good reason why functional programming is making headway now, despite being around for decades. Up until recently processor speed went up year by year, but there is a physical limit to how small transistors, and hence how fast processors can get. With that limit on the horizon chip-makers needed a different tactic. Rather than packing a single processor unit on a chip, they started putting in multiple processor “cores”. This multiplies the performance, in theory, but the software needs to be able to keep all those cores occupied. This is where functional programming comes in.

Writing software that performs multiple tasks in parallel is hard. Multithreaded programming does not only break your brain, it can also be the source of the most subtle and hard to track down bugs. Functional programming can make it a lot easier to avoid such bugs. In fact systems written in a functional style can often be made to work in parallel with only minimal changes. That’s quite something to get for free! Tasks that can be easily split up and parallelized across multiple cores and processors can also easily be distributed across multiple servers, making it much easier to scale popular services. The “map-reduce” paradigm that dominates Google-scale data processing is essentially functional.

2.1 Evocation

The simplest definition of functional programming would be “programming with values”. That doesn’t sound too daunting, does it? Although it does lead to the question, what are values? 1

We could try to answer that in depth, and confuse matters before we’ve even started. But let us start simple, with numbers. Numbers are values, for instance.

4
12
20

Merely writing down some numbers is, to be honest, not the most exciting thing we can come up with. So what else can we do? We can assign names to these values, i.e. numbers, and now anywhere we use the value we can just as well use its name2.

x = 4
twelve = 12
the_answer = 42
four = x

But just having those values around, named or not, still doesn’t really do much, does it? This is the point where functions come in. Let’s start with some simple arithmetic functions since you probably already know those.

Take a function, and feed it some values. The function does its magic, and will get back to you with a result. This is called applying the function to one or more values. What we get back is the function’s result or return value. Which is, necessarily, again a value.

1 + 1
# => 2
7 * four
# => 28
result = (3 * 4) / twelve
# => 1

What we have demonstrated so far is actually just (admittedly very) plain Ruby code. If we want to show the return value of an expression, we add it in a comment starting with # =>, as you can see here.

Isn’t this great? Turns out you’ve been doing functional programming since primary school!

So far so good, we’re getting warmed up. But so far we only know one type of values: numbers. Well, actually we have already come across another type of values. Can you guess? That’s right, functions!

Let’s try this, if functions are just values we can assign names to them.

plus = +

And we can apply functions to values, so let’s apply a function to other functions.

sum = * + /

Did you try that? Did it work?

 

Oh.

 

Turns out arithmetic operators like + and - and Ruby methods like puts and each aren’t really values. This is something that sets Ruby apart from more “pure” functional languages.

We could “valuefy” them but that’s a different story. Keep reading and we’ll eventually get to that. But Ruby does have functions that are simply values, take this function here. It takes a value of type String and returns a different value of type String. (Strings are values, I hope you’re noticing a trend here 3)

fn = ->(name) { "Hello, #{name}!" }

We can apply that function to a value, in other words: call the function with an argument, and it will return another value

fn.("World")
# => "Hello, World!"

We can even create and apply the function in one go.

->(name) { "Hello, #{name}!" }.("World")
# => "Hello, World!"

Unlike + or puts, this function does not have a name, it’s an anonymous function, also known as a lambda.

Lambdas will feature prominently in this text, as you might have inferred from the title. If you find the name confusing, try substituting it with anonymous function or function value. I can’t guarantee that it will make more sense that way, but it might.

We can store a lambda in a variable, so we can refer to it, but it’s still just a lambda. The difference with a traditional Ruby function4 is that the latter is defined by its name, it can not exist without it. In contrast, when we assign a lambda to a variable, it is the variable that has a name. The value it holds, the lambda, is still nameless.

def jack(x)
  # A Ruby method, this is not a value
end

If functions are values, and functions take values, then it follows that we can pass a function to a function, let’s see how that works.

hello_maker = ->(name) { "Hello, #{name}!" }
apply = ->(fn, value) { fn.(value) }

apply.(hello_maker, "Alonzo")
# => "Hello, Alonzo!"

So what’s going on here? Our function apply takes two values as arguments. The first value must be a function, the second value can be anything, as long as it can be safely passed to the first function. apply will then, as its name suggest, apply the given function to the second argument.

Obviously we could have written hello_maker.("Alonzo"), bypassing the need for an apply function altogether. Here’s a first glimpse as to why passing functions to other functions might be useful.

to_upper = ->(str) { str.upcase }
first_letter = ->(str) { str.chars.first }

equal_by = ->(fn, a, b) { fn.(a) == fn.(b) }

equal_by.(to_upper, "Almanac", "aLmAnAc")
# => true

equal_by.(first_letter, "Almanac", "aLmAnAc")
# => false

Now we’re talking! We’ll walk you through this code line by line.

The first function, to_upper takes a string, and it returns the same string, but all in uppercase. So to_upper.("Calamitous") would return "CALAMITOUS".

The second function simply returns the first letter of a given string, so "Fortuitous" becomes "F", "gratuitous" becomes "g".

Next up is equal_by, which is a bit different in that the first argument it takes, fn, should be a function. For the rest equal_by is very generic. Its other two arguments, a and b, can be of any type, as long as applying fn to them makes sense.

In our case both to_upper and first_letter expect a string, so we’ll go with Strings.

Now to find the value of equal_by.(to_upper, "Almanac", "aLmAnAc") goes something like this, step by step

equal_by.(to_upper, "Almanac", "aLmAnAc")

# evaluate equal_by

to_upper.("Almanac") == to_upper.("aLmAnAc")

# evaluate to_upper (x2)

"Almanac".upcase == "aLmAnAc".upcase

# call upcase on each string

"ALMANAC" == "ALMANAC"

# check if the two values are equal

# => true

Functions that take other functions as their arguments, or return functions as their return value, are called higher order functions or HOFs. equal_by is the first HOF we’ve seen, since it takes a function as one of its arguments.

The Other type of higher-order functions are those that return a function.

Let’s try that. Here’s a function that we’ll assign to the name create_adder. It takes a number, and returns a function.

create_adder = ->(n) {
                 ->(x) { x + n }
               }

add3 = create_adder.(3)
add3.(5)
# => 8

In Ruby, the last value in a function body is what is returned from the function. In this case the function body is ->(x) { x + n }

When we call create_adder.(3), then inside create_adder n has the value 3, so the function we get back is equivalent to ->(x) { x + 3 }.

Notice that n (with the value 3) only exists during the execution of create_adder. Once create_adder.(3) returns n is no longer available, but somehow that 3 got captured inside the function that was returned. We’ll dive deeper into how that works exactly when talking about closures.

To round it up let’s look at one more higher order function. This one both takes and returns a function. In fact it returns a function that is a lot like the one we pass to it, but with some behavior added on top. This type of HOF is called a function decorator.

maybe = ->(fn) {
          ->(x) {
            fn.(x) unless x.nil?
          }
        }

maybe_add3 = maybe.(create_adder.(3))
maybe_add3.(3)
# => 6

add3.(nil)
# => NoMethodError: undefined method `+' for nil:NilClass

maybe_add3.(nil)
# => nil

Here we create a maybe decorator that makes a function more resilient. Many functions don’t play well with nil. They want something to work with, and will throw a hissy fit when passed that most non-value of values, the nil.

maybe can make such a function slightly more well-behaved. It will still function like before for proper inputs, but will silently ignore any nils.

We have already covered a lot of ground in this introduction. Don’t worry if not everything has clicked instantly. The main goal was to give you a taste of what’s to come.

Hopefully this introduction has given you an idea of the flavor of functional programming. We will explore in much greater depth both the general principles of functional programming, and the mechanics that are available to us to program in a functional way in Ruby.

The Lambda Calculus, A Historical Aside

Back in the 1930’s at Princeton University, the American mathematician and logician Alonzo Church was trying to give mathematics more rigorous underpinnings. In the process he came up with a system called the Lambda-calculus. This later would form the basis for the first “functional” languages.

The Lambda-calculus is a formal system for reasoning about computation. It’s a tool for answering the question “Which mathematical functions can be computed?”. In lambda calculus there are no numbers, no strings, there is nothing except “lambdas”, which is the name Church gave to anonymous functions. Lambdas are merely defined by the arguments they take, and the function body.

This is what a lambda expression looks like

λx.xyz

This would be equivalent to the Ruby code

->(x) { x.(y.(z)) }

A lambda starts with the Greek letter lambda, λ, (hence the name), the arguments it takes, a dot, and then the body of the function.

The lambda calculus is a very austere system. All it has are variables and anonymous functions, literally nothing else. Keep in mind that this predates actual computers. It was a theoretical device, used with pen and paper to explore logic and computation. But despite that it had a tremendous influence on computer science as it developed. You could say that Church, and his contemporary Alan Turing, have created the standard model on which computer science is founded. The equivalent of discovering atoms and molecules in physics.

Two decades later John McCarthy came up with the programming language LISP. It was the first functional programming language, and takes several key ideas from the lambda calculus, especially the concept of functions that can be passed around like other values.