---
title: Function Composition in Ruby
teaser: 'Ruby 2.6 added function composition to the Proc and Method classes, allowing
  us to use some functional programming goodness in our Ruby code.

  '
tags: ruby
author: Tom Wey
published_on: 2019-05-06
---

Along with a number of other cool new features and performance improvements,
Ruby 2.6 added function composition to the [Proc] and [Method] classes. Today
we'll take a look at how this allows us to use some functional programming
goodness in our Ruby code.

# What is function composition?

So what is function composition? Simply put, function composition is the act
of combining existing functions to create new functions. It's a common
technique in functional programming languages like Haskell and Elm.

![graphic illustrating function composition](https://images.thoughtbot.com/blog-vellum-image-uploads/TKWRbS5HQlepnHI5KuJJ_functionComposition.png)

I'll borrow some syntax from Haskell to describe this in a more concrete way
(don't worry, we'll get to some Ruby shortly!) Let's say we have two functions:
`func1` and `func2`. The type signature for `func1` is:

```haskell
func1 :: A -> B
```

This means `func1` takes a value of type `A` and returns a value of type `B`.
The type signature for `func2` is:

```haskell
func2 :: B -> C
```

So `func2` takes a value of type `B` and returns a value of type `C`.  Using
function composition, we can combine these two functions, creating a new
function `func3` which takes a value of type `A` and returns a value of type
`C`:

```haskell
func3 :: A -> C
```

This works because the type of value returned by `func1` matches the type of
value `func2` expects as an argument. So our original value goes into `func1`,
the value returned is then passed to `func2`, which then returns our final
value.

[Proc]: https://ruby-doc.org/core/Proc.html
[Method]: https://ruby-doc.org/core/Method.html

# Let's write some Ruby!

So how does function composition look in Ruby? Let's look at a real(-ish) world
example using Ruby `Proc`s. Let's say we have defined the following:

```ruby
BENEFIT_COST_IN_CENTS = 1000 * 100
TAX_RATE = 0.4

subtract_benefit = ->(salary) { salary - BENEFIT_COST_IN_CENTS }

pay_tax = ->(salary) { salary * (1 - TAX_RATE) }
```

Invoking `subtract_benefit` subtracts the cost of a benefit from a salary:

```ruby
SALARY_IN_CENTS = 15_000 * 100

subtract_benefit.call(SALARY_IN_CENTS) # => 1400000
```

Invoking `pay_tax` subtracts the tax as a percentage from the salary:

```ruby
pay_tax.call(SALARY_IN_CENTS) # => 900000.0
```

Now we can compose these two with [`Proc#>>`] to create a new `Proc` which,
given a salary, calculates our take home pay after tax and benefits:

```ruby
calculate_take_home_pay = subtract_benefit >> pay_tax

calculate_take_home_pay.call(SALARY_IN_CENTS) # => 840000.0
```

This is the same result we'd get if we nested then calls:

```ruby
pay_tax.call(subtract_benefit.call(SALARY_IN_CENTS)) # => 840000.0
```

As the employee, we've worked this to our advantage by subtracting the benefit
amount before taking off the tax. If we pay tax and then subtract the benefit
cost we'll end up with a different result:

```ruby
less_beneficial_calculate_take_home_pay = pay_tax >> subtract_benefit

less_beneficial_calculate_take_home_pay.call(SALARY_IN_CENTS) # => 800000.0 😞
```

In this case we changed the order by reversing the order of the procs. We can
also control the ordering with the operator we use. `Proc#>>` goes from left to
right but its counterpart [`Proc#<<`] goes from right to left.

Let's wrap up the example by defining a `Proc` which formats the salary nicely:

```ruby
format_salary = ->(salary) { "$#{format('%0.2f', salary / 100.0)}" }
```

Now we combine this with `calculate_take_home_pay` to create a `Proc` which
takes a salary and returns a nicely formatted take home amount:

```ruby
formatted_take_home_pay = calculate_take_home_pay >> format_salary
formatted_take_home_pay.call(SALARY_IN_CENTS) # => "$8400.00"
```

[`Proc#>>`]: https://ruby-doc.org/core/Proc.html#method-i-3E-3E
[`Proc#<<`]: https://ruby-doc.org/core/Proc.html#method-i-3C-3C

# Pitfalls to Look Out For

When combining functions, we need to make sure that we're composing in such a
way that the types match. In our intro we borrowed some type signature examples
from Haskell, which is statically typed. The Haskell compiler will let us know
if we're trying to compose functions whose input and output types are not
cohesive, i.e. whose types don't match.

Ruby is a little bit more flexible, and we rely on duck typing rather than on a
static type system. But we can still combine functions in ways which don't make
sense, resulting in runtime errors.

Here we define two `Proc`s, `string_length` which takes a string and returns
its length, and `exclaim` which takes a string and capitalizes and adds a "!"
to the end:

```ruby
string_length = ->(string) { string.length }
exclaim = ->(string) { "#{string.upcase}!" }
```

Now we combine them into a function (of dubious usefulness) which takes a
string, exclaims it, and returns the length:

```ruby
exclaimed_length = exclaim >> length
exclaimed_length.call("ruby") # => 5
```

So far so good. Now let's see what happens if we reverse the order of
composition:

```ruby
exclaimed_length = length >> exclaim
exclaimed_length.call("ruby") # => undefined method `upcase' for 4:Integer
```

Oops! We calculated the length first, which gave us `4`. But then we tried to
call `#upcase` on the result, which doesn't make sense because `Integer`
doesn't implement `#upcase`. In this situation I often think of the analogy of
trying to fit together two mis-matching jigsaw puzzle pieces.

# Wrapping Up

One of the things I find interesting about programming languages is when ideas
from one language influence the way you write and think about other languages,
particularly those which use a different programming paradigm. So it's
great to see concepts from functional programming languages, like function
composition, make it into an object-oriented language like Ruby.

I don't think it's the right tool for every job, and as programmers we have a
responsibility to write code which is as clear and maintainable as possible.
But being able to succinctly combine discreet functions into more complex,
powerful functions is a tool I'm glad to have available.
