Lodash & Folktale Partial Function Differences

Introduction

At work I was trying to practice doing more composing of functions, and to do so you create a lot of partial functions, often through currying. Sometimes, the functions aren’t built for partial/currying, or the parameters are in an awkward order. Lodash offers partialRight instead.

However, I was trying to use all of Folktale’s functions instead of my normal use of Lodash in an effort to force myself to learn the differences. They both offer many of the same features like curry, partial, compose etc. but with different function signatures. I came to like the Folktale’s v2 version of partial called partialize and wanted to briefly talk about why.

Below, I’ll go over parsing a time & weather web service JSON response. We’ll show parsing the data using both Lodash and Folktale functional partials. This should teach you the difference between the 2 libraries approaches to function partials.

Curry & Partial Refresher

If you don’t know what currying and partial functions are, Eric Eilliot has a great comparison.

Currying is used to ensure a function with multiple arguments instead returns a function until all arguments are passed. This makes it easier to create reusable functions by passing only some of the arguments and getting a function or function partial back. This is useful for composing functions together, as well as reducing the amount of duplicated code in your unit tests.

Lodash Get

In Lodash, they have a get function. It works like a Maybe. A Maybe is a way to battle null pointers. Null pointers are when you attempt access data and it’s not there, and instead of giving you the data, it blows up instead. You return a Maybe from functions where data might not exist instead of the typical “wrap with a try/catch, attempt to access it, and log out if didn’t work, debug later”. This forces you to deal the missing data at the time you access it, and plan for a plan B if any (this could just be logging it’s not there), or design specifically for missing data.

Here’s some time JSON I’m getting from a time web service:

time: {
"error" : 0,
"error_message" : "-",
"time" : "2017-11-24 06:00:26",
"timezone" : "Pacific Standard Time",
"offset" : -480,
"daylight_savings" : "No daylight savings"
}

Using get, we’ll snag the time property off the Object. If the JSON is undefined because of a parsing error, or doesn’t have a time property, instead of throwing an error we’ll just fallback with an date of ‘now’ on the client machine the code is running on.

_.get(json, 'time', new Date().toString())

Lodash Partial

For weather, it’s a bit more complicated. We can’t provide a “default high temperature” unless we’ve gotten a successful result awhile ago. In a future article, we’ll create the cache. For now, we’ll just fallback to a question mark. Given we know both the paths to the temperature, we can preload those 2 parameters in a function partial, and just pass the JSON to it when we’re ready.

const getMaxTemperature = _.partialRight(_.get, 'main.temp_max', '?');
const getMinTemperature = _.partialRight(_.get, 'main.temp_min', '?');
const getTemperature = _.partialRight(_.get, 'main.temp', '?');
const getDescription = _.partialRight(_.get, 'weather[0].description', 'unknown conditions');
const getWindSpeed = _.partialRight(_.get, 'wind.speed', '?');

Since the 1st parameter to _.get is the dynamic JSON result, and the path, and the default value are the 2nd and 3rd parameter, we have to use _.partialRight instead of _.partial to ensure those 2 parameters are pre-filled, and there is a “hole” in the 1st parameter for the function that’s returned.

We’ll put ’em all together to get a weather string:

const getWeatherString = result =>
`High of ${getMaxTemperature(result)}, low of ${getMinTemperature(result)}.
Currently ${getTemperature(result)} with ${getDescription(result)}, wind ${getWindSpeed(result)} mph.`;

For good JSON, this’ll produce:

High of 30.2, low of 26.6.
Currently 28.89 with mist, wind 3.58 mph.

For now, just remember that we pre-filled the ‘getXTemperature’ functions with the path to check, and a default value if not found. At that time, I thought that was great; abstracting away the parsing details as well as a fallback value.

However, I’ve sometimes found that you want to provide that default value at the time you use the function, making it more reusable. This is especially true when composing functions via Folktale’s compose or Lodash’s flow/flowRight.

Folktale Partial

Folktale’s partial is interesting in that it differs from Lodash in 2 ways.

First, partialize returns a partial creating function, similar to curry. Lodash does this in 1 line of code: create the ready to be used partial function.

Second, there is no concept of left or right; you use that partial factory function to determine where the holes are; i.e. where you already have data to give, you do, and where you want the partial to accept arguments, you put holes. While curry in both libraries tends to favor more dynamic data to the right, Folktale’s partial is a lot more flexible.

We’ll still use Lodash’s get for the time, and instead use the “_” symbol for Folktale’s “hole” variable. This convention of using _ is from many FP languages like Elixir, PureScript, etc. that use it for “I don’t care”, “don’t worry about it”, or “a placeholder”.

const { get } = require('lodash');
const partialize = require('folktale/core/lambda/partialize');
const _ = partialize.hole;

To create our time Maybe using Folktale, it takes 2 lines of code vs. Lodash’ 1 so you can have more flexibility in argument order:

const getPartial = partialize(3, get);
const getTime = getPartial(_, 'time', new Date().toString());

The weather, we’ll do the same way we did in Lodash with 1 change: instead of providing the default values, we’ll defer to do that later.

const getMaxTempature = getPartial(_, 'main.temp_max', _);
const getMinTempature = getPartial(_, 'main.temp_min', _);
const getTempature = getPartial(_, 'main.temp', _);
const getDescription = getPartial(_, 'weather[0].description', _);
const getWindSpeed = getPartial(_, 'wind.speed', _);

This is a key feature; we can put a value in the middle and holes on the left and right. Lodash partial and partialRight require arguments, in order, in a line. Folktale doesn’t care where you put the holes. That’s 🔥!

const getWeatherString = result =>
`High of ${getMaxTempature(result, '?')}, low of ${getMinTempature(result, '?')}.
Currently ${getTempature(result, '?')} with ${getDescription(result, 'unknown description')}, wind ${getWindSpeed(result, '?')} mph.`;

Notice we’ve supplied the defaults in-line. For weather, we probably can package some caching logic into the partials. However, when using functions you didn’t create, which is often in JavaScript using libraries, Folktale gives you a lot more flexibility in creating function partials, even if the library authors followed currying best practice to put dynamic data towards the right.

The Lodash solution is to use rearg and then create a partial from that new function.

Conclusions

At first I thought the 2 functions, and manual hole creation process was a lot more work than Lodash’s simple 1 partial and partialRight 1 liners. Now I understand the power you get from doing it is that you can choose any order of arguments you want. For JavaScript, this is helpful because you’ll often use 3rd party libraries, many of which do not follow the curry best practice of dynamic data towards the end of the argument list. Lodash and Folktale work really well together, but it’s fun to use Folktale’s more more strict functions to learn more about them. Partialize ends up being a lot more flexible, and is worth the extra work in defining the function partials I think.

Leave a Reply

Your email address will not be published. Required fields are marked *