Navigation

Conditionally Composable Functions

I write a lot of Javascript at work, mostly in the Meteor frame­work. Our web app use a client side router, and we had this helper func­tion for fetch­ing a team from a slug in the route URL.

It looked some­thing like this:

function getTeam() {
let team;
team = Teams.findOne({slug: FlowRouter.getParam('slug')});
if (!team) {
const rec = RouterHelpers.getRecording();
team = rec && Teams.findOne(rec.teamId);
}
if (!team) {
const test = RouterHelpers.getTest();
team = test && Teams.findOne(test.teamId);
}
return team;
}

So we have a con­cept of Teams, Recordings, and Tests here. And in these three con­texts, this func­tion should be able to re­turn a team, if all the de­tails are al­right.

You see it looks kind of messy: repet­i­tive, de­pen­dent on or­der, not that main­tain­able if this one would grow, so I set out to refac­tor it with some func­tional think­ing in mind.

I iden­ti­fied the ob­vi­ous need for null checks and the flow of or­der. What if we could ab­stract out the guard parts? I.e. if (!team) { doThis() }. The flow of or­der can use the help of compose — a com­mon func­tion for com­pos­ing sev­eral func­tions into one (link to Underscore’s docs). In plain code:

const f = (val) => val + 1;
const g = (val) => val + 2;
const h = compose(g, f); // Same as g(f())
h(1); // => 4

You ba­si­cally get a sin­gle func­tion which is kick­started with a pa­ra­me­ter, and will pass each func­tion’s re­turn value on to the next one. Great for se­quences. It cor­re­sponds to the math­e­mat­i­cal:

h(x) = (g ∘ f)(x) = g(f(x))

So from that, I iden­ti­fied three func­tions I wanted to be in this chain:

  1. getTeamFromRoute
  2. getTeamFromRecording
  3. getTeamFromTest

in that or­der.

Now we could wire it up like the old if rid­dled ex­am­ple above, but then we’ve just re­placed some state­ments with func­tions — that is­n’t func­tional pro­gram­ming!

So we could come up with this be­low, where each func­tion checks the pa­ra­me­ter (result from pre­vi­ous func­tion in­vo­ca­tion):

function getTeam() {
const getTeamFromRoute = () =>
Teams.findOne({slug: FlowRouter.getParam('slug')})
);
const getTeamFromRecording = (team) => {
if (team) return team;
const rec = RouterHelpers.getRecording();
return rec && Teams.findOne(rec.teamId);
};
const getTeamFromTest = (team) => {
if (team) return team;
const test = RouterHelpers.getTest();
return test && Teams.findOne(test.teamId);
};
return _.compose(getTeamFromTest, getTeamFromRecording, getTeamFromRoute)();
// Like: getTeamFromTest(getTeamFromRecording(getTeamFromRoute()));
}

This is al­right but not great, I think. We still re­peat our­selves in check­ing the team in the two last func­tions, and the func­tions them­selves thus aren’t be­hav­ing in the same way — they have dif­fer­ent ar­ity (number of ar­gu­ments taken in the sig­na­ture).

What if we can ex­tract the null check and thus the de­ci­sion mak­ing into its own func­tion, and in­voke it out­side of our get­ter func­tions? Let’s try.

What we need is a sin­gle check to see if the re­sult from last in­vo­ca­tion is truthy or not. If it is, we should stop and re­turn. If not, pass on to the next func­tion in the chain. So we will need some kind of in­ter­cep­tor be­tween our func­tion calls in compose, which sniffs the re­turn value.

I came up with this:

function getTeam() {
const maybeReturnTeam = (callback) => (team) => team || callback();
const getTeamFromRoute = maybeReturnTeam(() =>
Teams.findOne({slug: FlowRouter.getParam('slug')})
);
const getTeamFromRecording = maybeReturnTeam(() => {
const rec = RouterHelpers.getRecording();
return rec && Teams.findOne(rec.teamId);
});
const getTeamFromTest = maybeReturnTeam(() => {
const test = RouterHelpers.getTest();
return test && Teams.findOne(test.teamId);
});
return _.compose(getTeamFromTest, getTeamFromRecording, getTeamFromRoute)();
}

Does it look weird? Perhaps, yeah. The new thing is maybeReturnTeam. In plain, old style Javascript, it would be:

function maybeReturnTeam (callback) {
return function (team) {
return team || callback();
};
}

A func­tion which re­turns a func­tion which per­haps in­vokes a call­back. So each callback in there will be one of our get­ter func­tions. And since maybeReturnTeam re­turns a func­tion, it’ll play well with compose, since it’s ac­tu­ally only the func­tion re­turned that will be di­rectly in­voked by the com­po­si­tion chain.

The maybeReturnTeam will also ef­fec­tively pre­vent other get­ter func­tions to be called if it en­coun­ters a truthy value in the first func­tion. Great!


So we got rid of all the if’s and cre­ated a nice, flow­ing chain of func­tions do­ing there thing. Sometimes it’s not worth it to go into crazy func­tional pro­gram­ming tech­niques in every­day code, since in a large code base and with a team, you need to keep read­abil­ity (and un­der­stand­abil­ity!) high. And one might ar­gue that the orig­i­nal code I had there at the top is ev­i­den­tally less code. But as a whole, FP makes to­tal sense to me once you learn how to re­think how you ac­tu­ally do pro­gram flows to­day.