Either monad to the rescue…

Lots of programming problems can be modeled by pipe-lining data through series of sequential or parallel processing steps. This data flow allows us to separate computational tasks into meaningful modules and
get more focused code base that is easier to debug and reason about. For example, a pipeline with three steps that takes input ‘req’ and successfully process it, would look like this:

success-pipeline

And if at any stage there is error during the processing, we would skip the rest and return error…

erro-pipeline

Now, there are many ways to implement error handling, but this can easily end up in a messy combination of conditional statements and/or throwing and catching exceptions to stop processing. This adds noise to the real code logic and makes composing tasks harder… I’ve recently started to use Either monad to help with this this problem and am very happy about the results:

var Either = require('data.either');
var log = console.log;
var handlers = [];
function use(f, p, t) {
handlers.push({ func: f, pred: p || false, trace: t || false});
}
// f, ep, m(a) -> m(b)
function run(f, ep, m) {
return m.chain(function(d) {
var r = f(d);
return ep(r) ? Either.Left(r) : Either.Right(r);
});
}
// hs, ep, a -> b | err
function runAll(hs, ep, d) {
var m = Either.of(d);
hs.forEach(function(h) { m = run(h.func, ep, m); });
return m.merge();
}
//---------------------------------------------------------------------------------
//@tests
//---------------------------------------------------------------------------------
var expect = require('expect.js');
log('testing: monad-pipeline-spike.js');
function f1(ctx) {
log('f1, counter before: ' + ctx.counter);
if(ctx.counter > 1) {
ctx.statusCode = 500;
return ctx;
}
ctx.counter += 1;
return ctx;
}
function f2(ctx) {
log('f2, counter before: ' + ctx.counter);
if(ctx.counter > 1) {
ctx.statusCode = 500;
return ctx;
}
ctx.counter += 1;
return ctx;
}
function f3(ctx) {
log('f3, counter before: ' + ctx.counter);
if(ctx.counter > 1) {
ctx.statusCode = 500;
return ctx;
}
ctx.counter += 1;
return ctx;
}
// setup
use(f1);
use(f2);
use(f3);
// run until failure
var result = runAll(handlers, function(d) {return d.statusCode !== 200;}, {counter: 1, statusCode: 200});
log(result);
//output:
//>f1, counter before: 1
//>f2, counter before: 2
//>{ counter: 2, statusCode: 500 }
view raw monadic-pipeline.js hosted with ❤ by GitHub

Here, I’m using Either implementation from the nice functional library Falktale

Till next time…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: