ShakaCode | ShakaCode Blog | Rails On Maui Blog | Rails | ReactJs | JavaScript | Webpack | Productivity |

Mixing Lodash-FP with Lodash


#1

Any opinions on mixing Lodash FP into a large project already using Lodash?

Concerns:

  1. Increased API surface area for every developer on the team.
  2. Increased JavaScript bundle size: How much bigger is the bundle? Any issues if we’re trying to minimize bundle size using lodash/babel-plugin-lodash
  3. Decreased runtime performance: Is lodash-fp as fast as plain lodash?

#2

Well it’s probably no surprise that I’m all for it. To your concerns, I think I have a solid counter-point for each one:

  1. It’s the same API, just remember that it’s curried, data-last, and immutable. That’s why there aren’t separate API docs for it.
  2. We’ve been using the full build this entire time anyway, so whether we use it or not we were putting it in the bundle. If we decide to use the babel plugin or do modular importing, then it’s no larger than importing the non-immutable method. So either way, there’s no difference.
  3. I haven’t seen any performance benchmarks on this, it’s sort of bike shedding.

JavaScript is great for functional programming because it has first class functions, and object oriented code is “sort of awkward” as the Mostly Adequate Guide puts it (link below). The problem is that currently its standard library is missing core functionality such as currying and immutability. Lodash FP (among others, such as ramda) solves that problem. By not allowing the use of this library, we’d be essentially saying functional programming is not allowed at ShakaCode. Considering our awesome embrace of new tech and methods, I find it hard to believe that we truly would want to have this as a policy.

Perhaps those interested in the topic could read The Mostly Adequate Guide to Functional Programming. I’ve already been able to make drastic simplifications to my code by embracing some of the basics I read about here.

Here is a simple example using the FP remove:

const myArray = [4, 3, 1, 6, 2, 5];

const sortedEvens = flow(remove(n => n % 2 === 0), sortBy);

const result = sortedEvens(myArray); // [2, 4, 6]

What if, in this case, myArray was actually taken from a React component’s state, meaning I had to be careful not to mutate it? You’d be fine with the above. The alternative when using the non-FP would be:

const myArray = [4, 3, 1, 6, 2, 5];

const myArrayClone = clone(myArray);
const evens = remove(myArrayClone, n => n % 2 === 0);

const result = sortBy(myArrayClone); // [2, 4, 6]

While the difference is only one line, you are actually importing another lodash method (so FP would actually be less size in this case), you are still having to perform a clone (so no performance gain), and I’m actually using fewer methods while, in my opinion, expressing my intent in a much clearer way (you expressed concern about the mental compilation required of developers).

And it’s this last point that is really the whole reason I’m doing it: I’m declaring what I want to have happen, sortAndJoin, instead of telling it how to do each step myself (declarative versus imperative coding).

Declarative, as opposed to imperative, means that we will write expressions, as opposed to step by step instructions.

Think of SQL. There is no “first do this, then do that”. There is one expression that specifies what we’d like from the database. We don’t decide how to do the work, it does. When the database is upgraded and the SQL engine optimized, we don’t have to change our query. This is because there are many ways to interpret our specification and achieve the same result.

(Mostly Adequate Guide to Functional Programming, Ch. 6)


#3

@robwise: Per your example, you had a typo, and maybe it’s better to just use filter:

const myArray = [4, 3, 1, 6, 2, 5];

const myArrayClone = clone(myArray);
const evens = remove(myArrayClone, n => n % 2 === 0);

// NOTE: prior example had myArrayClone, which is the ODDS!
const result = sortBy(evens); // [2, 4, 6]

How about:

const myArray = [4, 3, 1, 6, 2, 5];
const evens = _.filter(myArray, n => n % 2 === 0);
const result = sortBy(evens); // [2, 4, 6]

Your above example uses remove which is explicitly for mutation.

BTW, I still think it’s a potentially get idea to use lodash-fp, especially once the documentation website is done.

…and we still need to answer my 3 questions above.


#4

Yeah good points, lol guess I shouldn’t type examples so quickly.

I attempted to answer your 3 questions in the previous post (labeled 1, 2, and 3).

This blog post starts out being about simply why to not use chain (it basically requires the entire lodash library instead of allowing you to cherry pick), but over the course of the article, the author relays a lot of great information on lodash fp.


#5

I’ve been mixing Lodash-FP and lodash for some time now. It’s fine. I was doing this

const _ = require('lodash')
const fp = require('lodash/fp')

Over the weekend I’ve written a code-transform to help you refactor your codebase from lodash: lodash-to-fp.js.

It’s not perfect. I’ve taken a “better safe than sorry” approach so the transform does what it’s sure about, and leaves complicated stuff for you to solve manually. It went 80% of the way for me :slight_smile: