Functional programming - a brief intro

What it is and why I care

Taran "tearing it up" Bains • December 13, 2020

Canvas

Image by Free-Photos from Pixabay

When talking about programming paradigms, there are two words that often get thrown around: imperative and declarative.

Imperative code is code in which the programmer specifies to the computer exactly what actions they’d like it to take. Imagine that the computer were the driver of a car and you were the passenger. Your driver happens to be very well-meaning but incredibly clueless person when it comes to geography. Therefore, you are ultimately tasked with providing directions on how to get to your house (every left turn, every right turn, and every exit that would need to be taken… it is up to you to tell them which direction to take).

Conversely, declarative code is code in which the programmer is interested in what the computer produces rather than how it gets there. If we continue with our driving example from above, this would be you telling your driver to take you home and unlike the imperative example where the driver had no sense of direction of all, the driver knows how to navigate the streets and therefore it would be up to the driver’s responsibility to figure out the route that gets you home. All you really care about the end result, that is, you ending up at home (hopefully there’s a cute puppy there to greet you though because who doesn’t love being greeted by a puppy? 😍).

Functional programming is a declarative style of writing code.. Declarative programming is a style and Functional Programming is a flavour of this style. This means that the code, at least in theory, that is written in this style is more concerned with the what and not the how of program execution. You might be thinking to yourself, well why should I care about my code being more declarative? As long as it works and gets the job done, code is code, right? WRONG! Okay… you’re not entirely wrong. We live in the real world and working code beats code that doesn’t even exist. However, while there are pros and cons to both styles of programming, there’s one thing that both camps agree on: all things being equal, code that is easier for a human to understand is preferred, since humans (at least at the time of this blog post) are the ones maintaining said code. Arguably, one of the greatest benefits of declarative code is that it is easier for humans to understand. Refer to the below examples:

imperative-get-user-names.js
const users = [
    { name: "Manjot", age: 28 },
    { name: "Garneet", age: 25 },
    { name: "Carlen", age: 26 }
]

const names = []

for (user of users) {
    names.push(user.name.toUpperCase())
}

declarative-get-user-names.js
const users = [
    { name: "Manjot", age: 28 },
    { name: "Garneet", age: 25 },
    { name: "Carlen", age: 26 }
]

const names = users.map(getUpperCaseName);

The examples above are a lot of things (trivial being one of those said things) but they aptly demonstrate the key difference between imperative and declarative code. The first code block (like the second) has an array of users with various attributes. It then declares a new names array (which is empty) — signalling to the programmer that they should reserve some space in their brain for this variable because it will show up later on in the program. Finally, it loops over the list of users and pushes onto the names array (from earlier, remember! 😱) the result of user.name.toUpperCase().

The declarative example declares an array of users (exactly like the first example) and then declares a names array and assigns its value to be the result of mapping over the users with the getUpperCaseName function (gee, I wonder what that function does? -.-). Assuming you know that the map function on the Array prototype applies a transformation function (the argument to .map) and returns a new array with the newly transformed values, you would agree that the second example is the more readable piece of code.

Functional programming brings the mathematically proven theories of Category Theory to our code. This is code that by definition is backed by hundreds of years of mathematical research and discoveries. I admittedly don’t know much Category Theory but the idea of my code being mathematically provable to work correctly sure as hell beats the alternative of “well, I’m pretty sure this works because my tests pass”. That about covers the what but beyond FP being declarative, there are other reasons why I think it leads to more maintainable and ultimately better code:

  1. FP function signatures are more meaningful (they tell you all their dependencies upfront)
  2. FP is easier to test
  3. FP can lead to fewer bugs (linked to 2)
  4. FP enables more code reuse via composition

The points I listed above don’t really make sense (except for number one) if you don’t have some sort of understanding of Functional Programming concepts (like pure functions). Point 2 essentially is possible thanks to the idea of having pure functions. A pure function is a function that is deterministic and has no observable side-effects. You can think of side-effects as anything that impacts the outside world of a function. Things like printing stuff to the console, writing to disk, making xhr’s, and setting timeouts can be considered as side-effects. But wait, you might be thinking, if all my code is pure (has no side-effects) what good would my code be? That’s the right question indeed!

A program cannot be useful if it does not have some sort of observable side-effect. The trick to doing this with functional programming is separating out the impure procedural side-effecty code from the pure functional code in our applications. By doing this, we would be able to more easily test various functions inside of our application.

For example:

function submitForm(user: any) {
  return axios.post("/", { data: { name: user.name, food: user.favoriteFood } })
}

The function above accepts a user and makes a POST request to some endpoint with a subset of user data. Presumably the API we are posting to has denoted what values are acceptable to send over and anything else will be ignored or throw some sort of error to be sent back to the calling code. The current implementation does not lend itself to easily to testing as we are formatting the data to send and posting (a side-effect) in the same function. One way we could make this more testable and more functional would be to separate out the impure nonfunctional code from the pure functional code. For example:

  function formatFormData(user: any){
    return {
      name: user.name
      food: user.favoriteFood
    }
   }

   function submitForm(user: any) {
     return axios.post('/', { data: formatFormData(user) })
   }

In this example, you can see that we would now be able to predictably test, at the very least, that the data we intend to send to the server is tested and of the right shape. Fun fact, this code will also lead to fewer bugs as if we need to change the user data we’re sending over, we can change the code in one place (formatFormData) versus in multiple places such transformations occur.

Simply put, the reason I care about functional programming is that it can potentially lead to easier to read code, requires deeper thought (or at least I think it does), employs and favors things like composition which ultimately leads to more code reuse and that is equivalent to us writing less code. The less code we write, the less code we have to test, and potentially the less code that’ll cause bugs 😂.

Closing Thoughts

While JS isn’t a strictly functional language (like Haskell), we are able to leverage some of the concepts/benefits of Functional Programming in our JS code. Ways in which we can make our code more functional will be covered in a later blog post 😊. To learn more about Functional Programming in JS, check out the following resources:

  1. Functional Programming Light
  2. Functional JS
  3. JS Conf