Understand the basics of functional programming paradigm with Ruby examples

Recently, I’ve been reading Functional Programming in Scala by Paul Chiusano and Rúnar Bjarnason and I can really recommend it to anyone who wants to dive into functional programming concepts. The book starts with describing the basic features of FP to let you understand what is it all about. Here’s how the authors write about the book itself:

This book introduces the concepts and techniques of functional programming (FP). (…) Our goal is to give you the foundations to begin writing substantive functional programs and to comfortably absorb new FP concepts and techniques beyond those covered here. Throughout the book we rely heavily on programming exercises, carefully chosen and sequenced to guide you to discover FP for yourself.

So let me pull out some of these concepts here:

Function

An operation that takes an input and turns it into a specific output. The output value depends only on the arguments that are an input, so calling a function twice with the same value for an argument will produce the same result each time. A function is a relation between an input and a corresponding output.

Side effect

A side effect refers to the modification of some of state like:

reassigning a variable

modifying a data structure in place

writing some data to a DB or a disk

changing a state of some UI component

A side effect occurs when a function causes a non-local change to something other than its input and declared output.

Pure function

A function that has no observable side effects or call other side-effecting function. By observable we should mean modifying an external state outside a function. A function can never take a value and change it, as this would cause a side effect tangential to returning a result.

def add(a, b)

a + b

end

First-class functions

If a language supports first-class functions, that means it allows functions to be passed as arguments or returned from functions, assigning them to variables or storing them in data structures and treated like any other value in the language.

High order function

Higher-order functions are functions which take other functions as arguments or return them as values.

Partial function

Function which is not defined for some inputs. A function is typically partial because it makes some assumptions about its input that are not implied by the input types. A function may also be partial if it does not terminate for some inputs.

Partially applied function

Partial function application is the ability to take a function of many parameters and apply arguments to some of the parameters to create a new function that needs only the application of the remaining arguments to produce the equivalent of applying all arguments to the original function.

Strict function

Function whose parameters must be evaluated completely before they may be called. A function f is said to be strict if, when applied to a non-terminating expression, it also fails to terminate. Most programming languages have strict semantics, in which if any subexpression fails to have a value, the whole expression fails with it.

Eager evaluation is an implementation of the strict semantics. A kind of opposite is lazy evaluation.

Ruby is, of course, a strict language, but it gives us at least some ability to suspend strict evaluation in the form of blocks, procs, and lambdas. Each of them are ways of passing chunks of code around — that is, of defining code without immediately using it.

Referential transparency

An expression is referentially transparent if all its occurrences can be replaced by the result of evaluating this expression, without affecting the observable behavior of a program containing this expression. Only referentially transparent functions can be memoized (transformed into equivalent functions which cache results) because evaluating a function multiple times with the same arguments will always yield the same result.

Currying

Currying occurs when you break down a function that takes multiple arguments into a series of functions that take only some of the arguments. This goes hand in hand with a partial function application, which transforms a multi-argument function into a function that takes fewer arguments than it did originally. All the functions that accepted several parameters so far have been curried functions.

Closures and Lambdas

Lambdas are a language construct (anonymous functions), closures are an implementation technique to implement first-class functions (whether anonymous or not).

lambda — just an anonymous function defined with no name. Normally, we make a lambda with the sole purpose of passing it to a higher-order function.

closure — a first-class function that remembers the values of all the variables that were in scope when the function was defined and is able to access these variables even if it is executed in a different scope.

Recursion

Looping by calling a function from within itself. When you don’t have an access to mutable data, recursion is used to build up and chain data construction. This is because looping is not a functional concept, as it requires variables to be passed around to store the state of the loop at a given time.

Tail recursion (call)

A call is said to be in a tail position if a caller does nothing other than returning a value of the recursive call. For example, the recursive call to

factorial(number — 1, accumulator * number)

above is in a tail position since the caller simply returns the value of this recursive call. If, on the other hand, we say

number * factorial(number — 1)

factorial would no longer be in a tail position since the caller would still have work to do when factorial returned its result (namely, multiplying a number by it).

Lazy evaluation

Delaying processing values until the moment when it is actually needed. If, as an example, you have some code that has generated a list of Fibonacci numbers with lazy evaluation enabled, this would not actually be processed and calculated until one of the values in the result was required by another function, such as puts.

Subscribe to get the latest content immediately

https://tinyletter.com/KamilLelonek

Summary

I hope it’s already clear that you don’t have to use a different language to leverage functional programming. After reading this article you are familiar with FP concepts so you can use them in your Ruby code right now. Go ahead and try some of these patterns to see if they work for you and what benefits you can achieve. I believe you will gain a lot and your code will become more maintainable and better testable when you apply functional paradigm there.

FP is a truly radical shift in how programs are organized at every level — from the simplest of loops to high-level program architecture. The style that emerges is quite different, but it is a beautiful and cohesive approach to programming.

Part 2 available here:

https://medium.com/@KamilLelonek/functional-programming-with-ruby-2-2-c711a2db415c

Resources

Books

Gems