Advanced Functional Programming concepts made easy

Tal Joffe
Tal Joffe
Feb 4 · 7 min read

Usually, I talk and write about the basics of functional programming because I believe they are the easiest to apply and bring the most value.

But if you have the basics figured out and want to understand what all the cool Haskell developers are talking about, you came to the right place.

Philip Wadler — a cool Haskell developer

As always, I will try and explain the concepts in understandable terms and avoid the mathematical theory (because it is confusing and because I don’t know it very well 😝).

My examples will be in Javascript because it is what I use mostly these days but should make sense even to Java developers ( I’m also a Java developer, so I’m allowed to make fun of Java 😎)

This post is not trying to be very practical. I’ll focus more on explaining the terms and less about using them in production code (probably a topic for a future post 😄)

The topics I will cover are:

  1. Lambda calculus
  2. Functors and Monads
  3. Functional composition
  4. Point free Programming
  5. Currying
  6. Tail call optimization

Before we start, mumble to yourself the word “lambda” a few times to get in the right mood, and you’ll be ready to go 🙏🙏🙏

Lambda Calculus

Functional programming stemmed from a mathematical theory called “Lambda calculus” that was developed by mathematician Alonzo Church in the 1930s.
It was created roughly at the same time as the Turing Machine” and had equivalent computation capabilities. Unlike the Turing Machine, it is purely mathematical and uses only variables, function creation, and function application.

A lambda calculus expression can look something like this:

(λx.x+1)3

In this expression, we are creating a function that increments its argument, and we are applying 3 to the function. I will spare you from the more complex examples 😬

Functors and Monads

This will always be a Monad for me — a magical one-eyed toad

Monads are another example of a functional programming term that can be very intimidating, but in fact, could be easily explained.

Here is the simplest way I could think of:

  • Functor — wrapper type that allows you to use map()
  • Monad — a functor you can flatten
from monads in pictures

In the above illustration, the functor is the box. When you use map() you take what’s inside, apply a function to it, and put it back in. If there is nothing inside, you do nothing.

Using map() in most modern languages collections works in the same way.

Sometimes the function you are applying to the object is wrapping it in another box. Flatten makes sure you only have one box.

Two monadic types we have in Javascript are Array and Promise. Both behave similarly to the Maybe and Either Monads Respectively; Maybe means that I maybe have something in the box, and Either means I either have a success object or a failure object.

const doSomething = (data) => 
new Promise((resolve, reject) => {
// some logic
const result = someCalculation(x,y)
if (result) {
resolve(result)
return
}
reject(createErrorMesage('unable to create result'))

This usage of monadic chaining is sometimes referred to as “Railway Oriented Programming” because you are going on the “happy path” (.then().then().then()) until something is rejected and then we move to the “unhappy path” (.catch()).
Promise is available out of the box and is used mostly for async flows. If you want the same behavior for synchronous flows you can either use Promise explicitly like my example or use a Monad from one of the external FP libraries available

Functional Composition

Functional composition is one of the core principles of functional programming, and I touched on it in my previous post. When building a functional program, our building blocks are functions, and we compose them together to create our logic.

The math definition of function composition is simple

h(x) = (gf )(x) = g(f(x))

“h” is a function that is created by composing “g” and “f.”

The straight forward way to compose functions would look like this

const absolutePlusOne = increment(root(square(number)))

As this might get hard to read some of you would want to write like this

const absolutePlusOne = number => {
const squared = square(number)
const rootOfSquare = root(squared)
const result = increment(rootOfSquare)
return result
}

This is still a composition of function, but it introduces unnecessary intermediate variables. We can write it more functionally using several mechanisms:

// Using pipe operators (stage 1 in Javascript)
const absolutePlusOne = square |> root |> increment

When we define functions using only compositions of other functions without passing arguments (like in the pipe operator example) it is called point-free style (A.K.A Tacit Programming) — “point-free” basically means no arguments

Currying

To understand Currying, let’s start with two more basic terms, “high order function” and “partially applied function.”

A high order function is a function that either gets a function as a parameter (e.g., Array.prototype.map()) or returns a function as a result, or both.

A partially applied function is a high order function that uses some of the arguments and returns a function that expects the rest. Let’s see an example:

const add = (x, y) => x+y

Most JS developers will recognize this pattern as a closure. For those coming from a more OOP background, you can think of this technique as a “factory for functions.”

Currying is creating partially applied functions that always return a unary function (a function that only gets a single parameter) and thus making it easier to compose.

Currying Steph Curry

Some functional languages have a built-in mechanism for currying functions. In Javascript, we can either write our own or use external libraries like loadash or rambda

Tail Call Optimization

source: XKCD

Recursion is a widespread pattern in functional programming and is another way to build logic with a composition of functions

The problem with recursion is the infamous StackOverflow error 😱.

Every recursive function call goes to a part of the memory called “the stack,” and if we have many recursive calls, we might run out of memory.

The solution to that is the “Tail Call Optimization.” TCO is an optimization the compiler can do to avoid the StackOverflow error by replacing every function call with the next function call with aggregated parameters. To allow it to work, we need to have the recursive call as the final action in the function.

Let’s consider this implementation of a factorial calculation (x!)

const factorial = n => (n <= 1) 
? 1
: n * factorial(n - 1)

If we will try and call factorial(5) the call stack will look something like this :

factorial(5)
5 * factorial(4)
5 * 4 * factorial(3)
5 * 4 * 3 * factorial(2)
5 * 4 * 3 * 2 * factorial(1)
5 * 4 * 3 * 2 * 1
5 * 4 * 3 * 2
5 * 4 * 6
5 * 24
120

As you can see, for a large-number input, we can have a very deep stack and potentially run out of memory.

If we refactor the function to end with the recursion call like so:

const OptimizedFactorial = n => {
const accumFactorial = (n, accum) => (n < 2)
? accum
: accumFactorial( n - 1, n * accum)
return accumFactorial(n,1)
}

the compiler can use a tail call optimization to make sure that at any given point in time we have a single call in the call stack:

factorial(5)
accumFactorial(5,1)
accumFactorial(4,20)
accumFactorial(3,60)
accumFactorial(2,120)
120

Sadly in Javascript, though supported by ECMA Script, this optimization is only implemented by the Safari engine (so not available in Node and most browsers)

Summary

Functional programming can sound intimidating and impractical, and some aspects of it are both, but many terms and patterns can be clear with simple explanations.

I didn’t touch on everything, but I chose some common terms that most people trying to get into functional programming have heard and maybe didn’t fully comprehend.

I hope this was helpful and would love to hear comments here or on Twitter @TalJoffe

Additional reading

If you are looking for a one-stop-shop on functional programming in Javascript, I recommend reading this blog posts series by Eric Elliot.

Nielsen-Tel-Aviv-tech-blog

A publication by the Nielsen Tel Aviv Engineering team, where we talk about what we do and how we do things

Tal Joffe

Written by

Tal Joffe

Interested in software, people, and how to bring the best of them both @TalJoffe

Nielsen-Tel-Aviv-tech-blog

A publication by the Nielsen Tel Aviv Engineering team, where we talk about what we do and how we do things

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade