← Back to the homepage

# A Guide to Function Composition in Ruby

## by Paul Mucur on Wednesday, 8th May 2019

The release of Ruby 2.6 in December 2018 included the following “notable new feature”:

Add function composition operators `<<` and `>>` to `Proc` and `Method`. [Feature #6284]

In this post, I will explain “function composition” and how these new operators work, including how you can use them not just with `Proc` and `Method` but with any object that implements `call`.

## What is function composition?

Before we get into Ruby’s implementation of function composition, let’s be clear what it actually means. Wikipedia describes function composition as follows:

In computer science, function composition is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.

Let’s think of a simple example of a function: something that takes a single number as input and returns double that number, e.g.

``````double(x) = x * 2
double(2) = 4
double(3) = 6
double(4) = 8``````

Now let’s imagine another simple function that squares a number (that is, multiplies it by itself):

``````square(x) = x * x
square(2) = 4
square(3) = 9
square(4) = 16``````

If we wanted to first double a number and then square the result, we could call each function individually with our desired input, e.g.

``````double(2) = 4
square(4) = 16``````

As our functions only calculate their result from their given input and have no side-effects (see “referential transparency” for more information on this topic), we can combine them:

``square(double(2)) = 16``

For convenience, we could define a new function to do this operation for us:

``````double-then-square(x) = square(double(x))
double-then-square(2) = 16
double-then-square(3) = 36
double-then-square(4) = 64``````

Tada! We have composed the two functions `double` and `square` into a new one, the pithily-named `double-then-square`!

While composing functions yourself might seem relatively straightforward, some programming languages have a “first-class” notion of function composition which allow us to compose functions without having to define new functions ourselves. Perhaps the most concise example is Haskell’s function composition through the `.` operator:

``doubleThenSquare = square . double``

As in our previous example, this returns a new function which will call `double` and then call `square` with the result, returning the final result to us.

The order of operations might be confusing here: reading left to right, we first refer to `square` and then to `double` but when we call our `doubleThenSquare` function the order in which our component functions are called is in the opposite order. This is because the mathematical definition of function composition is as follows:

``(g ∘ f)(x) = g(f(x))``

This notation can be read “`g` after `f`” (or “`g` following `f`”) which might help when remembering the order of application when you call your composite function.

In a programming language where programs are largely written as a series of functions, having first-class function composition makes it easier for authors to compose behaviour from existing functions, possibly encouraging the breaking down of large functions into smaller, simpler parts.

So where does Ruby come into this?

## Functions in Ruby

While we’ve been discussing functions in pseudocode, we need to establish the equivalent building blocks in Ruby. Ideally we want functions that we can pass as arguments to other functions, store in variables and data structures and be able to return them from other functions: in other words, we want first-class functions.

The first obvious Ruby equivalent is `Proc`, described in the Ruby documentation as follows:

A `Proc` object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called. Proc is an essential concept in Ruby and a core of its functional programming features.

We can create a `Proc` in a surprising number of ways:

``````# Use the Proc class constructor
double = Proc.new { |number| number * 2 }

# Use the Kernel#proc method as a shorthand
double = proc { |number| number * 2 }

# Receive a block of code as an argument (note the &)
def make_proc(&block)
block
end

double = make_proc { |number| number * 2 }

# Use Proc.new to capture a block passed to a method without an
# explicit block argument
def make_proc
Proc.new
end

double = make_proc { |number| number * 2 }``````

(See “Passing Blocks in Ruby Without `&block`” for more information on this last constructor.)

We can use a `Proc` by calling its `call` method with any arguments we desire. There are also a few shorthand alternatives we can use for brevity:

``````double.call(2) # => 4
double.(2)     # => 4
double      # => 4
double === 2   # => 4``````

Note that this last form is particularly useful when using a `Proc` in the `when` clause of a `case` statement as `case` statements evaluate their various branches by calling `===`:

``````divisible_by_3 = proc { |number| (number % 3).zero? }
divisible_by_5 = proc { |number| (number % 5).zero? }

case 9
when divisible_by_3 then puts "Fizz"
when divisible_by_5 then puts "Buzz"
end
# Fizz
# => nil``````

`Proc`s also come in an extra flavour: a “lambda”. These have their own constructors:

``````# Use Kernel#lambda
double = lambda { |number| number * 2 }

# Use the Lambda literal syntax
double = ->(number) { number * 2 }``````

The key differences between “lambdas” and “procs” (that is, non-lambdas) are as follows:

• Calling `return` from a lambda will only exit from the lambda whereas calling `return` from a proc will exit from the surrounding method (and will throw a `LocalJumpError` if there is no surrounding method)
• In lambdas, the number of arguments must match the definition exactly (as in a method definition with `def`) but in procs, missing arguments are replaced with `nil`, single array arguments are deconstructed if the proc has multiple arguments and no error is raised if too many arguments are passed

We can demonstrate these differences with the following examples:

``````def lambda_with_bad_arguments
lambda = ->(x, y) { "#{x} and #{y}" }
lambda.call([1, 2, 3])
end

# ArgumentError: wrong number of arguments (given 1, expected 2)

def lambda_return
lambda = -> { return }
lambda.call

"This will be reached"
end

lambda_return
# => "This will be reached"

proc = proc { |x, y| "#{x} and #{y}" }
proc.call([1, 2, 3])
end

# => "1 and 2"

def proc_return
proc = proc { return }
proc.call

"This will not be reached"
end

proc_return
# => nil``````

The other Ruby feature that we can use as a first-class function is `Method`. This allows us to represent methods defined on objects (e.g. with `def`) as objects themselves:

``````class Greeter

def initialize(greeting)
@greeting = greeting
end

def greet(subject)
"#{greeting}, #{subject}!"
end
end

greeter = Greeter.new("Hello")
greet = greeter.method(:greet)
# => #<Method: Greeter#greet>``````

These can then be called in exactly the same way as `Proc`:

``````greet.call("world") # => "Hello, world!"
greet.("world")     # => "Hello, world!"
greet["world"]      # => "Hello, world!"
greet === "world"   # => "Hello, world!"``````

Finally, some Ruby objects can transform themselves into `Proc` by implementing `to_proc`. Ruby will automatically call this method on any object that is being passed as a block argument to another method with `&`.

``````[1, 2, 3].select(&:even?)
# is equivalent to
[1, 2, 3].select(&:even?.to_proc)``````

Ruby provides implementions of `to_proc` for the following objects out of the box:

• `Hash` will return a `Proc` that takes a key as input and returns the corresponding value from the hash or `nil` if it is not found
• `Symbol` will return a `Proc` that takes an object and will call the method with the symbol’s name on it
``````{ name: "Alice", age: 42 }.to_proc.call(:name)
# => "Alice"

:upcase.to_proc.call("hello")
# => "HELLO"``````

(For more information about implementing `to_proc` on objects, see “Data Structures as Functions (or, Implementing `Set#to_proc` and `Hash#to_proc` in Ruby)”.)

## The Ruby feature proposal

In 2012, Pablo Herrero proposed that Ruby should offer composition for `Proc` with the following example:

It would be nice to be able to compose procs like functions in functional programming languages:

``````to_camel = :capitalize.to_proc

``format_as_title = lambda {|val| "Title: " + val.strip.capitalize }``

Herrero provided a pure Ruby implementation which would add `<<` as a method on `Proc` and behave as follows:

``````double = ->(number) { number * 2 }
square = ->(number) { number * number }
double_then_square = square << double
double_then_square.call(2)
# => 16``````

That same day, a debate over the exact syntax Ruby should adopt for this feature began. Some argued for `+`, others for `*` (as a close approximation of Haskell's aforementioned syntax) and some for `|`. In October that year, Matz weighed in:

Positive about adding function composition. But we need method name consensus before adding it? Is `#*` OK for everyone?

Unfortunately, consensus was not forthcoming (with `<-` being added to the debate) over the next two years.

In 2015, after spending some time doing functional programming in Clojure and finding the dormant proposal, I contributed a series of patches to Ruby to implement composition on `Proc` and `Method`. Keen to progress the debate, I picked `*` as the operator:

``````double_then_square = square * double
double_then_square.call(2)
# => 16``````

It would be nice to be able to compose functions in both ways, like in F#, you can do g << f or g >> f, sadly this was rejected before.

These two operators would allow for both “backward” composition with `<<` and “forward” composition with `>>`.

“Backward” composition maps to the mathematical operator `∘` we discussed earlier so `g << f` is the same as `g ∘ f` meaning that calling the resulting composite function with an input `x` will call `g(f(x))`

``````double_then_square = square << double
double_then_square.call(2)
# => 16``````

“Forward” composition is the opposite of the above so `g >> f` is the same as `f ∘ g` meaning that calling the resulting composite function with an input `x` will call `f(g(x))`

``````double_then_square = double >> square
double_then_square.call(2)
# => 16``````

It might be helpful to think of the direction of << and >> as indicating how results flow from `f` to `g` and vice versa, e.g. the result of `f` is passed to `g` in `g << f` whereas the result of `g` is passed to `f` in `g >> f`.

Another three years passed and the feature was finally merged into Ruby 2.6 by Nobuyoshi Nakada who took my original patches and changed the operator from `*` to `<<` and `>>`.

## Function composition in Ruby

So now we know what function composition is and how it got into Ruby 2.6, how does it work?

There are two new operators:

Here are some examples of usage with our existing `double` and `square`:

``````double_then_square = square << double
double_then_square.call(2) # => 16
# or
double_then_square = double >> square
double_then_square.call(2) # => 16``````

These operators are implemented on both `Proc` (regardless whether they are lambdas or procs) and on `Method`.

Internally, the way composition works regardless whether you’re using `<<` or `>>` is to create a new `Proc` (preserving whether the receiver is a lambda or not) that composes our functions for us. The entire feature is roughly equivalent to the following Ruby code:

``````class Proc
def <<(g)
if lambda?
lambda { |*args, &blk| call(g.call(*args, &blk)) }
else
proc { |*args, &blk| call(g.call(*args, &blk)) }
end
end

def >>(g)
if lambda?
lambda { |*args, &blk| g.call(call(*args, &blk)) }
else
proc { |*args, &blk| g.call(call(*args, &blk)) }
end
end
end

class Method
def <<(g)
to_proc << g
end

def >>(g)
to_proc >> g
end
end``````

This means we can compose `Proc` and `Method` objects in various configurations:

``````(double >> square).call(2)
# => 16

(double >> square >> Kernel.method(:puts)).call(2)
# 16
# => nil

(Kernel.method(:rand) >> square >> Kernel.method(:puts)).call
# 0.010775469851890788
# => nil``````

It also means that if you start with a lambda, composing to the left and the right will always produce a new lambda:

``````square.lambda?
# => true

not_a_lambda = proc { nil }
not_a_lambda.lambda?
# => false

(square >> not_a_lambda).lambda?
# => true

(square << not_a_lambda).lambda?
# => true``````

Similarly, starting with a non-lambda `Proc` or a `Method` will never give you a lambda back regardless of the right-hand operand:

``````(not_a_lambda >> square).lambda?
# => false

(not_a_lambda << square).lambda?
# => false``````

We can also take advantage of calling `to_proc` on Ruby objects and compose these with regular `Proc` objects:

``````(
{ name: "Alice", age: 42 }.to_proc >>
:upcase.to_proc
).call(:name)
# => "ALICE"``````

The other key thing to note when composing is that the argument can be a `Proc`, a `Method` or anything that responds to `call`. This allows us to compose our own objects with `Proc`:

``````class Greeter

def initialize(greeting)
@greeting = greeting
end

def call(subject)
"#{greeting}, #{subject}!"
end
end

(
:upcase.to_proc >>
Greeter.new("Hello") >>
Kernel.method(:puts)
).call("world")
# Hello, WORLD
# => nil``````

Note this only works if your receiver is a `Proc` or a `Method` though, so you couldn’t put `Greeter` first in the chain above (as it does not implement `>>`).