I’ve always been about the bottom line. Uninterested in pseudo intellectual concepts, fancy terminology and hype. Instead, I always reach for the tools and technologies that help me ship code as soon as possible. This approach was initially productive — specifically when I was building smaller “proof of concept” applications.
Unfortunately, this approach did not scale. As I progressed as a developer I started feeling the law of diminishing return on my productivity. Setting up a project, and reaching basic functionality was fast. But the real problems started creeping up when my applications started growing in complexity. I found that as a project’s lifecycle advanced I was writing complex code. Code that I had written become harder to reason about. In order to understand it, I had to be extremely concentrated.
I had this itching feeling that a better, cleaner approach to developing software had to exist. I had heard whispers about functional programming, and how it allows developers to write more concise and elegant code. I was unknowingly exposed to functional paradigms and patterns for the first time while working with React and Redux. They both incorporated some of the principles, and I liked them. I read about FP — to my initial dismay I saw its paradigms were based on abstract mathematical concepts and that it was very prevalent in academia. Being that my goal is to ship products as fast as possible, this seemed like a counterintuitive approach to what I was trying to achieve. After 4 years in engineering school, I was pretty set on the opinion that academia only tackled theoretical problems, and was unlikely to ever help me in my day-to-day of building things.
But FP kept haunting me. Elegant solutions and paradigms were sprinkled online in all my favorite open source projects, blog posts and tutorials. I put my skeptecism aside and started delving into FP.
Although the concepts involve new jargon, and include a steep learning curve, I was amazed and really excited about this “new approach”. This series of articles shares my learning experience, and aims at extracting and summarizing the pearls of FP which enable a cleaner, more concise development experience. I will attempt to build an intuitive understanding of the patterns I discuss and frame the problems and the provided solutions as simply as possible, overstepping unnecessarily complex definitions. Learning FP has a reputation for being a bit daunting, but by breaking down the concepts into smaller bits, we will make the ideas easier to digest.
The main difference in FP in comparison to other programming paradigms is a declarative approach (FP) versus an imperative one. Before we dive into formal definitions, let’s explore the differences by looking at an example.
Does this code seem evil? It should! What are the similarities between the methods above?
- The main complexity of this code snippet derives from the fact that instead of telling the computer what we want it to do, we are instructing it on how to do it. Code that tells the computer how to operate — ie. go to the array at index i and mutate or swap a value is called imperative code.
- This code isn’t readable (😱😱😱). This is a toy example, but as your program grows and your functionality becomes more sophisticated, using for loops like this creates code that is non trivial, and requires our brain to analyze the inner working of the loop while keeping track of indexes, variables and more. Imperative code increases the cognitive load when reading, and over time makes it easier to faulter in reasoning and logic.
Let’s rewrite this snippet of code, but in a declarative manner.
.map? .reduce? What is this black magic?
First off, I promise that given the same input, these two methods produce the same output every single time.
A quick aside on the declarative snippet -
.map() is a method accessible from every array in JS. The .
map() method creates a new array with the results of calling a provided function on every element in the calling array.
reduce() is a method that applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
Don’t fret about these just yet. We’re going to explore these handy array-native methods in depths in upcoming posts. But it is clear that the declarative snippet is more concise than the imperative one. It’s also a lot easier to read. Instead of instructing the program on which indexes I want it to access etc, I am simply supplying an expression to
.map() and .reduce() (an anonymous function in our case) which tells the program what I want it do to every element in the array.
This declarative approach is going to serve us well across the board by:
- Learning and using patterns in your code that are well-known, understandable, and proven to keep away the mistakes that make code harder to understand.
2. Composing shorter, expressive and concise code. After all, the less code we write the less we have to debug.
Most importantly, these tools and paradigms are going to help us achieve our (my) ultimate goal of shipping products faster. Check out the next post, where we discuss functions in JS, why they are special and how their characteristics enable functional programming.
If this post was helpful, please click the clap 👏 button below to show your support! ⬇⬇
And most importantly! I am creating these tutorials using Mindflow.ai. Mindflow creates smart summaries of your workflow (nearly) automatically. It makes documenting my work a breeze! Sign up for the alpha release here.