Using functional patterns in Javascript

I am going to assume you have some interest in the functional programming and haven't become fully proficient at it yet.

Furthermore, I assume you use Javascript (optionally with Typescript) at work.

Getting started can be hard. For me, I can digest the first few chapters of a tutorial and then get completely lost. That is, without following along with the code samples and exercises. It's become really clear to me that I can't learn this without actively practicing it. Maybe, as with any language learning in general? :)

The practicing can be intimidating, too, but for different reasons. Unless you are working alone in your one-man startup, or are a hobbyist, you are interacting with peers. And chances are, your peers don't practice functional programming, or else you wouldn't need to read this blog post.

I've encountered a lot of resistance. The concepts beyond map/reduce are hard to grasp at first reading of a pull request. And if your colleagues can't understand what you meant, I suppose the rejection is justified.

Hence I found that an incremental, cautious approach work best, both for me and for my team. You can get started with a few fundamental patterns that everyone can understand and learn to appreciate.

Benefits of functional patterns in Javascript

There are myriad benefits to using functional patterns over procedural. Which ones are important to you largely depends on your values.

For me, it's maintenance and esthetics.

Maintenance benefits arise when functions are pure, which is to say, return the same output for the same input, when data is immutable so you don't have to trace it in a debugger to see where values changed, and the majority of the code can be easily verified in unit tests. (Which does not mean that I must unit-test everything, just that I can do that and the incremental costs of each unit tests are low and constant).

A code that is easily maintainable means I get to finish my work early and go out for a walk. How's that for a tangible benefit?

Esthetical concerns are not an everyday topic in our profession. But you do know beauty in code when you see it. My heart flutters when I see brief functions that do one thing and do it well, composed in an elegant hierarchy, all parts synchronize to serve the whole. When I can read the contents of a file top-to-bottom and easily follow along.

A code that is elegant and esthetically pleasing means I feel relaxed and happy at work. That should count as a sizable benefit, too, in our era of meditation apps and therapists on speed-dial.

Start with first principles

You can dip your waters in the functional waters without knowing the full theory behind it. I know I don't.

The principles matter, though.

One of those that matter to me is a separation between code that chews on some data from code that sucks it in or spits it out.

Code that lies on input and output boundaries is dangerous, error-prone, impure. Code inside can be safe, error-free (to a degree you care about), and elegant.

I like this quote from an introductory guide to FP in Javascript:

"[W]ith functional programming, we generally try and work out the logic of what we’re trying to achieve first, before we do anything that has potential side effects."

That's it - being aware of those fundamentally different features of your code can pay long-lasting dividends.

Your plan of action

Let's work on an example.

Suppose you have a microservice that takes a customer order arriving as a web request, make sure it's legit, and save it to a database or something. Further assume it's a Node.js / Express app.

Here is how I would plan it on a very high level:

  1. Input layer (impure) - examine the request, see if it's valid, and transform it into a data structure for processing
  2. Inner layer (pure) - perform data transformations on the customer order such that we can work with it
  3. Output layer (impure) - persist the created customer order and provide a response to the web request

Our job will be to make the outermost layer as thin as possible, such that the majority of the code can be made pure and maintainable.

Taking it in

A vanilla Express controller has very few ways to ensure that the request data coming it is valid, sensible, or actionable.

module.exports = function(req, res) {
if (!req.body.customerId) [
return res.status(400).send({status: "FAILED", errors: ["missing parameter: customerId"]})
]
// ... and so on
}

This sucks.

At one point, I was doing a manual validation using a dictionary of required attributes of the incoming data structures, thereby implementing my own, home-made type enforcement system. Trust me, not worth the pain.

const _ = require("lodash");
// ...
const required = ["customerId", "productId", "quantity", "deliveryDate"];
if (!_.every(required, attrib => _.has(req.body, attrib))) {
return res.status(400).send({status: "FAILED", errors: ["one or more missing parameters..."]})
}
// ...

I have learned to embrace Typescript, define interfaces for the valid request payloads, and use Tsed.io to enforce them.

@Controller('/orders')
class MyCtrl {
//..
@Post('/')
create(@BodyParams() order: OrderDto) {
// at this point, inputs are sane and nominally valid
}
}

That lets me push the formal validation of the inputs out of my controller and into the framework / library code. Let someone else unit-test that, this is not my concern.

OK, so we have our order parameter, and presumably need to fetch some data from the database to run further validations on it, such as: is the item still on sale, what's the current price, etc.

I like to do all the database fetching early on, up-front, and only do the processing on the data afterward, making sure the processing functions do not touch any IO (database or otherwise).

The disadvantage can be making unnecessary database queries - if the first validation fails, you might not need the other data you fetched.

One way to circumvent that is to use map, filter, and reduce functions on arrays, combined with the railway-oriented programming technique.

const [error1, customerWithOrder] = customerOrError(order);
// ...
async function customerOrError(order: OrderDto) {
const customer = myDatabase.findCustomer({id: order.customerId});
return customer === null
? [[{error: "Customer not found"}, []]
: [[], [{ order, customer }]];
}

The next database query and transformation does not happen if the first query failed to find a customer, because customerWithOrder will be an empty list.

const [error2, orderWithProduct] = customerWithOrder.map(productInfoOrError);
// ..
function productInfoOrError(customerWithOrder) {
const product = myDatabase.findProduct({ id: customerWithOrder.order.productId});
return product === null
? [[{error: "Product not found"}], []]
: [[], [{ customer: customerWithOrder.customer, order: customerWithOrder.order, product }]];
}

This pattern is possible, however has several disadvantages. I don't like to pretend to work on lists when I know the list will have at most one element. And, I like to chain the operations such that I don't need to declare short-lived intermediary variables.

The ideal flow would read similar to this:

const [ firstError, orderInfo ] = customerOrError(order).map(productInfoOrError)
.map(priceInfoOrError)
.map(deliveryInfoOrError);

return firstError === null
? processedOrder(orderInfo)
: errorResponse(errors);

A couple of improvements and refactorings are in order to make that happen.

Refactorings, getting to "Either"

The "hydration" functions return a tuple of one-element lists, which are hard to read literals. Let's make a function that creates them.

function leftOrRight(error, value) {
return [[error], [value]];
}

The functions would then be a little easier on the eyes:

async function productInfoOrError(customerWithOrder) {
const product = myDatabase.findProduct({ id: customerWithOrder.order.productId});
return product === null
? leftOrRight({error: "Product not found"})
: leftOrRight(undefined, { customer: customerWithOrder.customer, order: customerWithOrder.order, product });
}

Better, but what about the null checking? We could factor that out, too.

function leftOrRight(input, error, output) {
return input !== null
? [[], [output]]
: [[error], []];
}

function productInfoOrError(customerWithOrder) {
const product = myDatabase.findProduct({ id: customerWithOrder.order.productId});
return leftOrRight(product, {error: "Product not found"}, { customer: customerWithOrder.customer, order: customerWithOrder.order, product });
}

BUT - we can't chain the map calls just yet, though. The tuple isn't smart enough.

Let's drop the lists and create a helper class we will call... Either. And see if we can do with just one and not having to deal with both Left and Right explicitly.

class Either
{
constructor(left, right) {
this._left = left;
this._right = right;
}

map(fn) {
return this._left !== null
? this
: new Either(null, fn(this._right));
}
}

The constructor takes two values: left and right. We'll use left for the error value and right for the "good" value.

function productInfoOrError(customerWithOrder) {
const product = myDatabase.findProduct({ id: customerWithOrder.order.productId});
return product === null
? new Either({error: "Product not found"})
: new Either(null, { customer: customerWithOrder.customer, order: customerWithOrder.order, product });
}

The null check is back in but the intent of the function is clearer I think.

We can further refactor this to hide the Either class and get rid of both the null checks and literal nulls floating around in the code.

function leftOrRight(input, error, output) {
return input !== null
? new Either(null, output)
: new Either(error);
}

And go back to this (but with improved behavior):

function productInfoOrError(orderInfo) {
const product = myDatabase.findProduct({ id: orderInfo.order.productId});
return leftOrRight(product, {error: "Product not found"}, _.merge({}, orderInfo, { product }));
}

Looks like we are on the right track. However, if you try to chain three or more .map calls, you'll realize that each call creates an additional Either instance wrapping the instance of Either returned by the previous .map call, creating the infamous "Russian dolls" situation.

The article on the Either monad linked above contains the full solution but I'll go with a simpler approach. I'm not trying to implement the full "monad", or else my procedurally-minded colleagues will crucify me. I want an object with a map method that propagates the values down the chain of processing without any nesting.

Refactoring the map function, we get:

/**
@param fn: any -> Either<any>
*/

map(fn) {
if (this._left !== null) return this; // nothing to do, we have an error
const val = this._right.constructor.name === "Either" ?
this._right.value() :
this._right;
return fn(val);
}

The parameter to the map method must be a function that takes an arbitrary input (which we could later constrain with Typescript interfaces if we wish) amd returns an Either for this to work.

One final piece is missing - how do we get out the value (good or bad) from Either?

value() {
const val = this._left || this._right;
return val.constructor.name === "Either" ?
val.value() :
val;

As with the map function, I must be careful to "unwrap" the actual value, depending on whether this Either instance contains a "raw" value (this will be the case when the very first Either is created) or another Either.

That's it! Each function that performs a validation or sources data from a database will return an Either instance that either has the data you'll need later or has some error info.

const inputs = customerOrError(order).map(productInfoOrError)
.map(priceInfoOrError)
.map(deliveryInfoOrError)
.value(); // get the output from the Either created by the last function call

return _.has(inputs, "error") ?
errorResponse() :
processedOrder(inputs);

We don't yet know what the processedOrder function does. We do know, however, that it will work with immutable data as we're leaving the impure IO layer and moving into safer spaces.

What can we do there? It's getting late, and so I'll save that for the next article!