What is function composition in Ruby?

Introduction

What’s function composition? Why does it matter?

In functional programming languages, such as Haskell, we are able to define functions and compose them to create new functions by combining their behaviors. For example:

fizzBuzz n = mod n 3 == 0 || mod n 5 == 0

fizzBuzzSum = sum . filter fizzBuzz

main = putStrLn $ show (fizzBuzzSum [1..999])
-- 233168

When we want to combine two or more functions, we can use the . operator in Haskell. What will happen is the value will be chained to the other(s) through one function, which s in a value after every function is applied.

This possibility gives us a degree of flexibility, as we are able to define new functions by re-using previously defined functions to create a new one. Thumbs up for productivity!

What about Ruby?

Ruby is a multi-paradigm language. It’s true that it includes some functional features, but it doesn’t have high order functions. Well, at least not the kind we are used to in Haskell or Javascript.

Ruby has lambdas and Proc objects, which are interchangeable in many cases:

numbers = (1..999).to_a
fizz_buzz = -> (n) { n % 3 == 0 || n % 5 == 0 }
filter = -> (fn, array) { array.select(&fn) }
sum = proc { |array| array.reduce(:+) }
puts sum.call(filter.call(fizz_buzz, numbers))

If you pay attention, fizz_buzz is declared as a lambda, while sum is declared as a Proc object. Under the hood, they are both represented as a Proc. So far so good.

Object-oriented everywhere!

In object-oriented languages, classes, objects, and methods are used to represent behaviors instead of functions. This is the way we use Ruby daily because, in Ruby, everything is an object.

class FizzBuzz
def call(n)
n % 3 == 0 || n % 5 == 0
end
end
puts FizzBuzz.new.call(1)
puts FizzBuzz.new.call(15)
puts FizzBuzz.new.call(30)

It achieves the same but, how would we compose objects with lambdas in a seamless way?

The to_proc is the protocol for converting an object to a Proc object:

class FizzBuzz
def to_proc
-> (n) { n % 3 == 0 || n % 5 == 0 }
end
end
puts FizzBuzz.new.to_proc.call(3)

As you can see,almost everything can be solved just with… functions. :)

References

Free Resources

Attributions:
  1. undefined by undefined