Concepts behind the Functional Programming Paradigm Explained

This one is going to be quite different as we enter into uncharted territories...

...well, as far as our blog's content goes.

No JAMstack, no JavaScript frameworks.

We're even leaving the realm of object-oriented programming.

I'm truly happy to demystify with you a subject I've been obsessed with lately, to be quite honest.

This is an overview of the functional programming paradigm.

In this piece, I want to explore some of the core concepts and fundamental ideas behind it. Concepts that you can start applying right now, to most programming languages.

I'll try to illustrate this along the way to go as smoothly as possible.

I really hope this gets you as excited as I am about what the functional paradigm can bring to the future of web development.

Disclaimer: I'm not by any means an expert functional programmer. I'm still in my early days with this paradigm as I only started to mingle with it around a year and a half ago. This probably will feel arcane to you at first but please bear with me as I honestly would consider functional as one of the biggest breakthrough in my logical thinking.

So without any more bantering, let's get into it.

What is functional programming?

Let's call it FP, for the cool kids.

Let's be lazy, because FP loves laziness, and refer to a quote to define the subject:

Functional Programming has many different definitions. A Lisp programmer's definition is vastly different from a Haskell perspective. OCaml's FP bears little resemblance to the paradigm seen in Erlang. You will even find several competing definitions in JavaScript. Yet there is a tie that binds -- some blurry know-it-when-I-see-it definition, much like obscenity (indeed, some do find FP obscene!) [...]

Brian Lonsdorf

While being as meta as one can get, this definition will actually start to make more and more sense as you adopt the FP style.

Besides being from Brian Lonsdorf, a big functional evangelist, this quote is taken from the foreword of "Functional Light JS", a book by the JS guru Kyle Simpson. If you're a web developer and you're anywhere serious in your interests to learn FP this should be your first go-to book.

functional-light-javascript

If you are indeed serious and struggle to find the time to learn FP then quit reading this post (you fool) and start reading this book instead, like for real.

Functional paradigm core concepts

We won't really touch any of the deep academical math roots of FP, this is something you might dive in naturally if you eventually pick up the paradigm but I don't think this is necessary right now.

We will rather focus on concepts that can be applied to most programming languages. With these, you can already start to use it in your day-to-day, incrementally.

Functions are not what you think they are

Before jumping into anything specific I want to prime you on some fundamentals about the term "function" in FP. A function in FP is much more strict than the classical definition you would get with the imperative paradigm.

If you never did FP before the chances are that this sounds true to you:

function == method == procedure == subroutine.

Well, not anymore pal. You should adopt a more mathematical approach to functions from now on.

Mathematical approach?

In a math function, there's no scope nor global state. There can't be any information accessed beside the input variables.

Which means, if you write f(x) = x^2 on a piece of paper, on the sandy soil of Mars or define the function on a computer, well, it will always evaluate to 25 for x = 5.

If you achieve this you'll end up with all the magic benefits of the mathematical world (hint; there's a lot more there than you might think).

A function should be pure

Pure means that given the same inputs a function should always return the same output, it's deterministic.

Let's put emphasis on always again. It means that those are not considered pure:

  • IO operations
  • Web requests,
  • Anything that can throw an exception

Now, some purely functional programming languages like Haskell will enforce this rule while some are more flexible. You might ask what on earth is going to be the purpose of your program if you can't do all of these. Well, the answer is that you actually can but in a very specific manner.

The concept is too advanced for the purpose of this post so we won't be covering it, but if you continue your FP journey you will definitely stumble on this on your own. ;)

What are the implications of this?

The sole outside interaction a function call can have is with its return value.

If you were to replace the function call by its return value this would not ever cause a difference in your program, this is called referential transparency.

Using functions this way decreases heavily the amount of information your brain has to load in order to understand what the logic does, thus making you more productive in the long run.

It makes it so you don't have to mentally compute the whole current state of your program at time X. You simply have to look at the input of the function and you will be sure of the current state.

Now, honestly there are more "rules" to a strict function definition in FP but it's the only one I think you should know for now.

Category theory applied to programming

As I said, functional programming takes its roots in math but more specifically in category theory.

This branch of mathematics mainly aims at "understanding the processes that preserve the mathematical structure."

Why does it have anything to do with coding? It turns out that any data structure is also a mathematical structure. Well, for most of us developers, our job consists mainly in modifying data structure, over and over again.

Understanding the processes

Basically, we're talking about any transformation that is done to the data inside the data structure.

Let's check an example.

So let's say we do this in OOP:

var nbrs = [1,2,3,4,5];
var res = [];

for (let i = 0; i < nbrs.length; i++) {
  if(nbrs[i]%2 == 0){
    res.push(nbrs[i] * 2);
  }
}

console.log(res);
//[4, 8]

Here's what the code does verbosely:

  • creating a new array (same structure as the prior array)
  • if the number is even we multiply by 2 and add it to the new array

Now, if we analyze this code with the "processes" and "structure" we just talked about we'll come to these conclusions:

  • We have two processes:
    1. We filter out odd numbers
    2. We multiply each number by two

That's it, two processes, one structure. It fits very well the branch of math we just talked about.

So how would it look like in FP?

var filterOutOdd = (nbr) => nbr%2 == 0
var multiplyByTwo = (nbr) => nbr * 2

var res = [1,2,3,4,5]
  .filter(filterOutOdd)
  .map(multiplyByTwo)

console.log(res);
//[4, 8]

Insights from this functional programming example

Honestly, nothing is too game changing in this example but let's try to get some insights from this.

You can see a new function used directly on the array, the map one. This function is usually explained quite trivially by saying "it lets you apply a function for each element of a list".

While it's true for the map implementation of an array there's way more to it.

Let's put it this way instead: the map function gives a way to apply a function to something wrapped in a certain context, it is left to that very context to implement the method the way it makes sense.

A very good intro to these concepts is offered here. It's a little bit more theoretical but still very accessible.

Let's read this again attentively: "[map] is a way to apply a function to something wrapped in a certain context".

Now let's rephrase that according to our example: "[map] is a way to apply a function to each value wrapped inside the array".

You can see we came back to the initial trivial definition but we now understand the abstract concept behind it.

The whole point here is not only to know how to use the Array.map function, but to grasp how it can be that a generic data structure such as an array can give an abstract utility function such that it works with any data inside it.

Then and only then, you will start to feel the soothing light of functional programming—before this realization, FP will mostly feel like hell, although a somewhat fun hell (nothing like PHP).

Here's a good image showing what your learning process will probably look like: FP-learning-curve

Perseverance will pay, big time.

Composability & reusability

The last concept you should have in mind to kickstart your FP journey is something we've been silently pointing at from the start: composability.

By expressing your data manipulations as granular as you can, you will naturally create small logic building blocks that will give a lot more composability than what you are probably used to—put it this way: granularity breeds composability.

Composability is fundamental to great programming because it's a condition to achieve proper reusability. Once you start to fiddle more and more with FP principles you'll start to do things over and over and you'll want to add some "meta" utility functions such as compose, curry, memorize, etc.

These are probably not worth using for you at the moment but you should definitely give them a look.

Closing thoughts & takeaways

It turns out separating the functions that modify data and the order that you apply them is a pretty good idea. It isolates your concerns and reduces the noise. FP naturally guides you this way.

You can think or even look at some code you already wrote and check if these principles fit. My bet is that they do for the majority of the code you write. Only some architecture concepts are way harder to model in FP and I don't think it's good for you to jump into this right now.

The point is that the whole sphere of "business logic" you usually write can easily benefit from these principles without major changes.

Jumping into the functional paradigm

A really good library to play around with is Ramda.js, just reading their documentation will give you really good insights.

Here are other references I encourage you to explore to get started & to take your functional programming knowledge further:

But hey, the biggest advice I can give you is to go at it incrementally and ask questions if you're stuck.

Now, here's two challenges you should try to do in order:

  • Don't write any for loops for the next month
  • Once you've done that, code functions such as filter, map & reduce yourself. This will you give a pretty good challenge and force you to understand what's happening under the hood.

Once you've done this, I would like for you to come back right here and tell me how's the experiment going. I would really appreciate getting the conversation going so hit the comment section below for any comments or questions!

And above all, happy functional coding. ;)


If you've enjoyed this post, please take a second to share it on Twitter.

Suggested posts: