Functional Programming In Ruby (Complete Guide)

Maybe you just heard about functional programming & have some questions.

Like…

  • What is functional programming exactly?
  • How does it compare to object-oriented programming?
  • Should you be using functional programming in Ruby?

Let me answer these questions for you so you can get a better idea of how this works.

What Is Functional Programming?

It’s not just a fad or a fancy word, it’s an actual programming paradigm that has been around for a long time but it has regained popularity recently.

And the basic ideas behind this paradigm are easier to understand than you think.

In functional programming we avoid changing state & we try to write “pure” functions.

Avoiding state change means that these functions don’t change anything outside of the function, no instance variables, no changing some object that was passed in…

None of that!

In a functional programming language (like Haskell) all data is IMMUTABLE.

There are things like variables, but they behave more like in the mathematical world. Once a variable is given a value, the compiler won’t allow you to redefine this variable with another value.

functional programming

Benefits Of Functional Programming

Immutability is the main advantage of functional programming because mutable data can lead to subtle errors that are hard to track.

Example:

def all_different_from_first?(arr)
  first = arr.shift

  arr.all? { |n| n != first }
end

arr = [1,3,5,7,9]

p all_different_from_first?(arr)
# true

In this example I want to find out if all the elements in the array are different from the first element.

To make this work we need to remove the first element from the array & at the same time save this element so we can compare it against the rest.

How can we do that?

We are working with an array & if you take a look at the list of available methods you will find that the Array#shift method does exactly what we want.

It works just fine until…

…you look at the value of arr after calling the method once:

all_different_from_first?(arr)
# true

arr
# [3,5,7,9]

Surprise!

The array lost one element (1) & we didn’t notice.

That’s how sneaky this kind of mutability bugs can be.

Fixed version:

def all_different_from_first?(arr)
  arr[1..-1].all? { |n| n != arr.first }
end

Functional vs OOP

Should we all just adopt functional programming?

It may seem like all this immutable state makes functional programming the complete opposite of OOP, and in a sense it is, but there is still a way that the two programming paradigms can work together.

So no, there is no need to rush & go full-on functional programming. Ruby is designed for OOP anyway, so you would be fighting against the grain.

The good news:

You can still use the best ideas from functional programming & apply them to your Ruby code.

Let’s talk about how to do that.

Reduce Mutability As Much As Possible

One way to do that is to STOP using attr_accessor, stick only to attr_reader.

After you do that then you need to keep an eye on strings, arrays & hashes.

There are methods that will change these objects:

  • Most methods ending in ! (like gsub!)
  • delete
  • update
  • clear
  • shift / unshift / pop / push

The first step is to be aware of these methods.

If you have to use one of these methods you can work on a duplicate object.

Given a string & a clone of that string:

str = "abcd"
dup = str.dup

We get these results when we clear the duplicated string:

dup.clear

# str => "abcd"
# dup => ""

This keeps the original string safe.

Partial Application

There is more to functional programming than immutable data & pure functions.

Like the partial application of functions, also known as “currying”.

Example:

def add(a,b)
  a + b
end

add_five = method(:add).curry[5]

add_five.call(5)
# 10

add_five.call(20)
# 25

Notice how the add method takes two arguments, but by using the curry method we can “preload” one of the arguments.

Then we get a lambda that we can call with just the 2nd argument.

Here’s another example:

list = (1..10)

greater_than = ->(x,y) { y > x }.curry

list.select(&greater_than.(5))
# [6, 7, 8, 9, 10]

list.select(&greater_than.(8))
# [9, 10]

One more example:

divisible_by = ->(x,y) { y % x == 0 }.curry

list.select(&divisible_by.(5))
# [5, 10]

list.select(&divisible_by.(2))
# [2, 4, 6, 8, 10]

Summary

You have learned about functional programming, the core of it is pure functions & immutable data, it’s just a way to think about your code & not completely incompatible with OOP.

Thanks for reading, don’t forget to subscribe to the newsletter if you haven’t yet! 🙂

5 thoughts on “Functional Programming In Ruby (Complete Guide)”

  1. Your blog is good. But isn’t it very ironical that you are running a ruby blog on a WordPress forum built on php. It seems like Tim Cook using Android phone personally. 🙂

    • It’s just practical 🙂

      And wordpress has very good plugins for blog marketing that make things a lot easier.

      No need to reinvent the wheel.

  2. Great post. I’ve been wanting to dive more into functional programming, and this is a great article to get your feet wet.

  3. Very nice. I was doing a bit of FP in Ruby recently. Have you tried to implement High Order Functions and Functors yet? Passing in functions and getting functions back is pretty cool, although a bit more difficult to learn and apply at first.

    I recommend the book Functional JavaScript https://www.amazon.com/Functional-JavaScript-Introducing-Programming-Underscore-js-ebook/dp/B00D624AQO . The concepts are applicable regardless of the language and it’s a very well written book.

Comments are closed.