Think Functionally in Javascript
Tags: functional, programming, javascript, computerscience
Published: 2021-09-08 12:15:50

## Introduction

In this post, I have used a way of not using any complex jargons/terms like functors, monads, monoids, applicatives, currying etc., but explain basic concepts to start with Functional Programming (FP) sooner than you can think. I tried this approach to train my colleagues and the response was tremendously appreciative. They comprehended the concepts because it sounded familiar to their usual programming way, but with a pinch of mindset change.
 
## First steps
I will first try to make you comfortable by not introducing  FP concepts right away. Instead, I will start with what FP made me do.
It made me,
- change my programming mindset
- un-learn a lot of programming concepts
- think of what to do rather than how to do
- focus on only one thing at a time
- reduce branching in my code
- focus on the separation of concerns
- think in terms of mathematical expressions (no rocket science here, it is simple)
 
### What can we deduce from the above?
As a result, I followed 6 basic rules in my learning to develop more FP compliant code. Functions always exhibit the following properties,
1. They are first-class citizens
2. They obey single responsibility principle
3. They are side-effect free
4. They are easily testable
5. They are declarative
6. They are composable
 
According to me, the above rules are good enough for someone to get started with FP. I call them the foundations to start FP. Let's take a deep dive into each of the foundations with examples.
 
## Functional foundations

### 1. Functions are first-class citizens

The simplest foundation of all. In FP, functions are everywhere, I mean, literally everywhere,
- Functions can be variables
```javascript
const myFunc = function () {
  console.log('This is a function!')
}

console.log(typeof myFunc) // function
myFunc() // This is a function!

// Arrow function way
const myArrowFunc = () => {
  console.log('This is an arrow function!')
}

console.log(typeof myArrowFunc) // function
myArrowFunc() // This is an arrow function!
```

- Functions can be passed as arguments to other functions
```javascript
const sayHello = function(text) {
  return `Hello ${text}`
}

const saySomething = function(fn, text) {
  return fn(text)
}

saySomething(sayHello, "Hello World!") // Hello World!

// Arrow function way
const saySomething2 = (fn, text) => {
  return fn(text)
}
```
 
- Functions can be returned from other functions
```javascript
const sayHello = function (text) {
      return `Hello ${text}`
}

const saySomething = function (fn) {
	return function(text) {
    	return fn(text)
    }
}

const inviteSomeoneWithMessage = 
      saySomething(sayHello) // (text) => sayHello(text)

inviteSomeoneWithMessage("World!") // Hello World!

// Arrow function way
// Here, first call to saySomething2 returns a function
// (text) => fn(text)
const saySomething2 = (fn) => {
  return (text) => {
    return fn(text)
  }
}

// Arrow function shorthand
const saySomething3 = fn => text => fn(text)
```

  
### 2. Functions obey single responsibility principle

In compliance with the previous foundation we need to make functions obey single responsibility principle. Now, what does that mean?

A function that obeys single responsibility principle should do one thing only. This comes from the famous SOLID principles, where, S = Single responsibility. The same thing goes for functional programming as well. Let us have a look at some examples.

Here, we have a `validateFormValues` function which validates the HTML form input values onSubmit.

```javascript
// This function validates the form input fields - fullName and phone.
function validateFormValues(formValues) {
    const { fullName, phone } = formValues
    
    if(!fullName) {
      	alert('Name is required')
      	return false
    } else if(fullName.length <= 3) {
      	alert('Name length should be greater than 3')
      	return false
    }
  
    if(!phone) {
      	alert('Phone number is required')
      	return false
    } else if(phone.length === 10) {
      	alert('Phone number should be of 10 characters')
      	return false
    }
  
    return true	
}
```

```javascript
// Validates full name only
function isValidFullName = (fullName) => {
    if(!fullName) {
      	alert('Name is required')
      	return false
    } else if(fullName.length <= 3) {
      	alert('Name length should be greater than 3')
      	return false
    }
  
    return true
}

// Validates phone number only
function isValidPhone = (phone) => {
    if(!phone) {
      	alert('Phone number is required')
      	return false
    } else if(phone.length === 10) {
      	alert('Phone number should be of 10 characters')
      	return false
    }
  
    return true
}

// Consolidates the validations
function validateFormValues(formValues) {
    const { fullName, phone } = formValues
    
    if(!isValidFullName(fullName)) {
      	return false
    }
  
    if(!isValidFullName(phone)) {
      	return false
    }
  
    return true	
}
```
 
There are loads of benefits of creating single-responsibility functions, which, we will eventually see in further foundations.

### 3. Functions are side-effect free

First let's discuss what's a side-effect? Simply stated, they are things that mutate function state outside its local environment.

```javascript
let num = 0

function incrementNumber() {
   return num + 1
}

incrementNumber() // returns 1
incrementNumber() // returns 2
```

The above function `incrementNumber` is not side-effect free because it increments a global variable. We have no way to say conclusively what the return value will be when the function is called. What does this tell us? It tells us that our `incrementNumber` function is not predictable. It does not return the same output for the same input. Thus, it is not a pure function. 

**To make a function side-effect free, we need to make it pure.** 
 
```javascript
let num = 0

function pureIncrementNumber(value) {
   return value + 1
}

pureIncrementNumber(num) // returns 1
pureIncrementNumber(num) // returns 1
```

The above function `pureIncrementNumber` is now pure because the function now increments the local value and not the global one. `pureIncrementNumber` has become deterministic, pure and predictable. Because it is predictable, we can easily test it, which brings us to the next foundation.

### 4. Functions are easily testable

Let us consider the below validation function.

```javascript
let num = 0

function incrementNumber(value) {
   const data = value + 1
   console.log('data = ', data)
   return data
}

incrementNumber(num) // returns 1
incrementNumber(num) // returns 1
```

Now, this may look pure but it isn't because `console.log` is a global function (a side-effect). So if we had to test this function,

```javascript
it('should return 1 if 0 is the input') {
    const input = 0

    const output = incrementNumber(input)
  
    expect(output).toBe(1)
}
```

The above test will pass but you will never know what was printed. If someone in your team or even yourself happened to change the console log value, you will never know because the test never asserted it. How to make sure that the test checks for the exact logged value?

```javascript
let num = 0

function incrementNumber(value, logger = console.log) {
   const data = value + 1
   logger('data = ', data)
   return data
}

incrementNumber(num) // returns 1
incrementNumber(num) // returns 1
```

Here, `console.log` is passed to the `incrementNumber` function as a dependency. Also, note that a default value is passed to it as well, so that it always has a value. 
Now, the test would be,

```javascript
it('should return 1 if 0 is the input', () => {
    const input = 0
    const consoleLog = jest.fn()

    const output = incrementNumber(input)
  
    expect(output).toBe(1)
    expect(consoleLog).toHaveBeenCalledWith(`data = ${input}`)
})
```

I chose this kind of an example for a reason. Observe that the side-effect `console.log` which made the function impure has been passed as a dependency. Passing it as a dependency, has made `incrementNumber` a pure function.

**Pure function and testability go hand-in-hand. If a function is pure then it will be easily testable.**

### 5. Functions are declarative

Declarative comes from "Declarative Programming". What does it mean? 

As per Wikipedia,

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sp3xqbbfeq5q6vedwb6v.png)
 
It means, your code should focus more on "what has to be done" rather than "how something has to be done".

Let us understand this by a simple example.

```javascript
function validateLoginForm(values) {
   const { userName, password } = values
   const errors = {}
   if(!userName || userName.length === 0) {
      errors.userName = "Username is required"
   } else if(userName.length < 8) {
      errors.userName = "Username should be at least 8 characters"
   }

   if(!password || password.length === 0) {
      errors.password = "Password is required"
   } else if(password.length < 6) {
      errors.password = "Password should be at least 6 characters"
   }

   return errors
}
```

The above function does a lot of things, it does not obey the single-responsibility foundation, it is not pure, because it is mutating the errors object, thus making it difficult to test. Some may feel that testing is easy but hang on, let us calculate the number of unit tests required for `validateLoginForm` function(we will need to test each of the branching conditions),

| Code coverage           | No. of tests                 |
| ----------------------- | ---------------------------- |
| `if(!userName &#124;&#124; userName.length === 0)` | 4 tests     |
| `if(userName.length < 8)` | 2 tests                    |
| `if(!password &#124;&#124; password.length === 0)` | 4 tests                    |
| `if(password.length < 6)` | 2 tests                    |

As you see, in total we have ended up writing 12 unit tests for such a simple function and that is excluding any `null`/`undefined` checks.

How can we improve this code and make it declarative so that we test it easily? The answer is to make it obey all the above foundations.

```javascript
const isNull = (value) => value === null
const isTextLengthThanLessThan8 = (text) => text.length < 8
const isTextLengthThanLessThan6 = (text) => text.length < 6
const isTextEmpty = (text) => text.trim().length === 0

function validateUserName(userName = '') {
    if(isNull(userName)) {
       return "Username is required"
    }

    if(isTextEmpty(username)) {
       return "Username is required"
    }

    if(isTextLengthThanLessThan8(userName)) {
       return "Username should be at least 8 characters"
    }

    return
}

function validatePassword(password = '') {
    if(isNull(password)) {
       return "Password is required"
    }

    if(isTextEmpty(password)) {
       return "Password is required"
    }

    if(isTextLengthThanLessThan6(password)) {
       return "Password should be at least 6 characters"
    }

    return
}

function validateLoginForm(values = {}) {
    if(isNull(values)) {
       return {}
    }

    const { userName, password } = values
    return {
       userName: validateUserName(userName),
       password: validatePassword(passwrod)
    }
}
```

In the above refactored code we just write text for `validateUserName` and `validatePassword` functions because they are the only ones with branching.

| Code coverage           | No. of tests                 |
| ----------------------- | ---------------------------- |
| `isNull(userName)` | 2 tests     |
| `isTextLengthThanLessThan8(userName)` | 2 tests                    |
| `isNull(password)` | 2 tests                    |
| `isTextLengthThanLessThan6(password)` | 2 tests |

That is *8* tests. Also, we would need to write *2* tests for `validateLoginForm` function to check the integration. We need not test `isNull`, `isTextLengthThanLessThan8` and `isTextLengthThanLessThan6` because these are functions that do just one job and they do it well. 
In total, we need *10* tests to have a sufficient code coverage. 

The above validation can still be improved by one of the most powerful and important foundations, which we will see next.

### 6. Functions are composable

**Composition/Function composition**, one of the powerful foundations of FP. 

As per Wikipedia,
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p781z68yjcm6m63gawnl.png)

Let us have a look at an example
```sh
cat result.txt | grep "composition" | tee file2.txt | wc -l
```

The above Linux command uses a `|` (pipe) operator and is a usual way of composition in Linux.

Let us compose one of the functions `isTextEmpty` from the previous foundation.  

```javascript
const isTextEmpty = (text) => text.trim().length === 0
```

Let's apply all the above mentioned foundations and break down this function.

```javascript
// trim:: string => string
const trim = str => str.trim()

// size::string => int
const size = str => str.length

// isEqualToZero::int => boolean
const isEqualToZero = value => value === 0

// isTextEmpty::string => boolean
const isTextEmpty = (text) => isEqualToZero(size(trim(text)))

isTextEmpty('hello') // false
isTextEmpty('') // true
```

As you can see, `isTextEmpty` in the above code,
- is a composed function
- does only one thing
- is declarative
- easily testable
- side-effect free

And most importantly the function is a first-class citizen.

Tip: We can make the `isTextEmpty` function more declarative by using [compose](https://ramdajs.com/docs/#compose) function from [Ramda](https://ramdajs.com/).
```javascript
const isTextEmpty = compose(isEqualToZero, size, trim)
isTextEmpty('hello') // false
isTextEmpty('') // true
```
You can also use another similar function [flowRight](https://lodash.com/docs/4.17.15#flowRight) from [Lodash](https://lodash.com/).

Let's have a look at the same validation example used in the previous foundation, but now with composition.

```javascript
import { compose, isNil, isEmpty, trim, length, cond, always, T as stubTrue, filter, gt } from 'ramda'

const isTextLengthLessThan = (ltValue) => compose(gt(ltValue), length, trim)
const isEqualToZero = value => value === 0
const isTextEmpty = compose(isEqualToZero, length, trim)

const validateUserName =
    cond([
        [isNil, always("Username is required")],
        [isTextEmpty, always("Username is required")],
        [isTextLengthLessThan(8), always("Username must be minimum of 8 chars")],
        [stubTrue, always(undefined)]
    ])


const validatePassword = 
    cond([
        [isNil, always("Password is required")],
        [isTextEmpty, always("Password is required")],
        [isTextLengthLessThan(6), always("Password must be minimum of 6 chars")],
        [stubTrue, always(undefined)]
    ])

const validateValues = ({
    userName,
    password
}) =>  filter(Boolean, {
    userName: validateUserName(userName),
    password: validatePassword(password)
  })

const validateLoginForm = cond([
    [isEmpty, always({})],
    [stubTrue, validateValues]
])

validateLoginForm({}) // {} 
validateLoginForm({userName: '', password: ''})  // { userName: 'Username is required', password: 'Password is required' }
validateLoginForm({userName: 'some_username', password: 'password123'}) // {}
validateLoginForm({userName: 'user', password: 'password123'}) // { userName: 'Username must be minimum of 8 chars' }
validateLoginForm({userName: 'some_username', password: 'pass'}) // { password: 'Password must be minimum of 6 chars' }
```

The above code,
- has functions as first-class citizens
- obeys single responsibility
    - all functions do only one thing
- is side-effect free
    - all functions are pure
- is easily testable
- is declarative
    - all function names are self explanatory and sometimes comments are not needed
    - implementation is abstracted to make the code more understandable
- is composable
    - small functions composed to form a bigger one

**Notice that, there is no branching (if else statements) in the above code, thus preventing different code paths. As a result, testing of this code becomes super easy. In fact, we can only test `validateLoginForm` for different input variations. No tests needed for other functions because they strictly follow the foundations.**


## Conclusion

- FP is not a new thing, it has been there in programming space since many years. It makes you look at a problem differently, change your existing mindset and make you unlearn a lot of programming concepts that were deeply ingrained in you. 
- FP makes your code predictable and thus makes writing unit tests easier. It makes you code with testing in mind.
- Every foundation obeys the foundation above it.
- FP makes you get away from imperative code.
- The foundations I mentioned above are my way of learning FP easily and quickly. These foundations will act as stepping stones for advanced concepts of FP like immutability, functors, monads, monoids etc.,

## Extra points

There are couple of things to note from the code examples,
1. Libs like **Ramda** and **Lodash** offer a lot a utility functions that make your journey into FP easy.
2. FP creates highly predictable code, thus leading us to focus only on the input and end result, without worring about implementation details because the logic does just one thing.

## References

1. Mastering JavaScript Functional Programming - by Federico Kereki
https://www.oreilly.com/library/view/mastering-javascript-functional/9781839213069/

2. https://fsharpforfunandprofit.com/video/ by Scott Wlaschin