What is Lodash/fp, even?
Tags: javascript, functional, lodash
Published: 2019-11-20 14:33:51

Hey!

This post is aimed at people who use lodash and want to try the FP variant but maybe want/need some guidance on what's the point of using the FP variant.

I will demonstrate the difference between the fp and non-fp variants using lodash `_.cond` for easy grasp of the topic.

___At first, we will use non-fp lodash in examples.___

---

## _.cond

### What is _.cond and why should I be interested?

From documentation:
> Creates a function that iterates over pairs and invokes the corresponding function of the first predicate to return truthy. The predicate-function pairs are invoked with the this binding and arguments of the created function.

Key points: 
* returns function
* pairs have to be functions
* both functions in a pair are called with whatever you call the function returned from `_.cond`
* top to bottom evaluation

`_.cond` is basically a __glorified switch statement__. It looks like this:

```javascript
var getColor = _.cond([
  [checkFn1, resultProvidingFn1],
  [checkFn2, resultProvidingFn2],
  ...
])

var color = getColor(valueToCheckAgainst)
```

Let's see it in real action:

```javascript

var isBetween0and5 = (value) => _.inRange(value, 0, 5)
var isBetween5and10 = (value) => _.inRange(value, 5, 10)

var returnRed = () => 'red';
var returnGreen = () => 'green';
var returnBlue = () => 'blue';

var returnTrue = () => true;

var getColor = _.cond([
  [isBetween0and5, returnRed],
  [isBetween5and10, returnBlue],
  [returnTrue, returnGreen] // default case
])

var color1 = getColor(3) // red
var color2 = getColor(7) // blue
var color3 = getColor(15) // green

```

Great! Everything's working! But...

There is a lot of code for such simple thing, don't you think?

Maybe until this point, it wasn't clear for you why does lodash have methods such as `_.stubTrue`, `_.stubFalse` or `_.constant` when, in fact, you can just type the values yourself. 

But... Can you?

You see, `_.cond` accepts functions so putting something like `[isBetween0and5, 'red']` into predicates wouldn't work.

With this in mind, the example above can be rewritten like:

```javascript

var isBetween0and5 = (value) => _.inRange(value, 0, 5)
var isBetween5and10 = (value) => _.inRange(value, 5, 10)

var getColor = _.cond([
  [isBetween0and5, _.constant('red')],
  [isBetween5and10, _.constant('blue')],
  [_.stubTrue, _.constant('green')] // default case
])

var color1 = getColor(3) // red
var color2 = getColor(7) // blue
var color3 = getColor(15) // green

```

That's better! But...

If you look at these functions:

```javascript
var isBetween0and5 = (value) => _.inRange(value, 0, 5)
var isBetween5and10 = (value) => _.inRange(value, 5, 10)
```

You can see they are basically just returning the result from `_.inRange`. Maybe we can use it directly?

Great, so you start typing this:

```javascript
var getColor = _.cond([
  [_.inRange(value, 0, 5), _.constant('red')]
])
```
...when you come to realise that there is no `value` in scope you could use.

So now you're thinking: "I just remove the `value` argument and it will pass it down anyway!"

Which is true, the value would get passed down, except... 

__You already invoked the function using ().__

This would crash because `_.cond` would expect a function where you provided a value (by calling the function).

Okay hold on... so how to actually accomplish this without the wrapper function?

There are 2 ways:

### _.curry

`_.curry` is a method that takes a function and curries it. For those who don't know what currying is, it's basically this:

```javascript

function add (a) {
  return function (b) {
    return a + b
  }
}

add(2)(3) // 5

```

Curried function is a function that accepts N arguments and won't give you result without providing N arguments - instead, it will return another function that accepts the rest of arguments.

Let's revisit our code once again:

```javascript

var curriedInRange = _.curry(_.inRange)

var getColor = _.cond([
  [curriedInRange(_, 0, 5), _.constant('red')],
  [curriedInRange(_, 5, 10), _.constant('blue')],
  [_.stubTrue, _.constant('green')] // default case
])

var color1 = getColor(3) // red
var color2 = getColor(7) // blue
var color3 = getColor(15) // green

```

Looking good! But why do we have to use that lodash placeholder (`_`) in first argument for `curriedInRange` ?

The problem is, non-fp lodash methods do not follow the __iteratee-first, data-last pattern__ which is required in functional programming (it means that data are the last argument to the function).

So... what this placeholder inside curried function does is basically "Okay, here is a placeholder, fill in the other arguments as they are and return me a function that will replace that placeholder with a value.". That way, we can finally get our function to use in `_.cond`!

Yay! Everything works! 🥳

But there is a better way:

## Lodash/fp 

Lodash/fp has the same functionality as non-fp lodash but its methods are all __curried__ and follow the __iteratee-first, data-last pattern__.

This enables us to drop all the ceremony like before and write:

```javascript

// using lodash/fp variant

var getColor = _.cond([
  [_.inRange(0, 5), _.constant('red')],
  [_.inRange(5, 10), _.constant('blue')],
  [_.stubTrue, _.constant('green')] // default case
])

var color1 = getColor(3) // red
var color2 = getColor(7) // blue
var color3 = getColor(15) // green

```
Tadaaa! All working and clean.

In this last example, what happened was the following:

`_.inRange(0, 5)(valueSuppliedByCond)`

(remember, the `_.inRange` method is now curried and follows __iteratee-first, data-last pattern__)

Here you can see why having data last is so important - because you're not calling the functions directly, you just provide them and they get called elsewhere with some value.

This example was aimed at `_.cond` but it does apply everywhere in functional programming. (`_.flow` being a very good candidate also).

You can look up more info about lodash/fp here: https://github.com/lodash/lodash/wiki/FP-Guide

Since this is my first post here, sorry for formatting. Please leave me some feedback on what you like/dislike, or some questions regarding the subject :)


Thanks for reading! Hope you enjoyed it.