Following on from my introduction to monads in JavaScript, and before I get into how they apply to asynchronous programming, I’d like to take a quick detour to improve the usability of the tools we’ve built up. Recall we have a function for composing functions:
var compose = function(f, g) {
return function(x) { return f(g(x)) };
};
We have some ‘debuggable’ functions:
// sine :: Number -> (Number,String)
var sine = function(x) {
return [Math.sin(x), 'sine was called.'];
};
// cube :: Number -> (Number,String)
var cube = function(x) {
return [x * x * x, 'cube was called.'];
};
And finally we have the unit and bind functions for the ‘debuggable’ monad:
// unit :: Number -> (Number,String)
var unit = function(x) { return [x, ''] };
// bind :: (Number -> (Number,String)) -> ((Number,String) -> (Number,String))
var bind = function(f) {
return function(tuple) {
var x = tuple[0],
s = tuple[1],
fx = f(x),
y = fx[0],
t = fx[1];
return [y, s + t];
};
};
These let us compose our debuggable functions to create new debuggable functions:
var f = compose(bind(sine), bind(cube));
f(unit(3)) // -> [0.956, 'cube was called.sine was called.']
This is all well and good, but we should really be able to compute sin(x^3) as a one-liner. With the code we have, this expression would be:
bind(sine)(bind(cube)(unit(3)))
This is hardly convenient, especially when I tell you that the equivalent Haskell expression is:
return 3 >>= cube >>= sine
This reads like a pipeline: take 3, pass it through cube, then pass the result of that through sine. In Haskell, unit is called return, and bind is actually an operator (or infix function) called >>=. The operator >>= doesn’t just convert functions into composable form, it takes a monadic value – in our example, a (Number,String) tuple – and a debuggable function, and deals with unpacking the value from the monad, applying the function to it, and combining the result with the monad in a meaningful way.
Using our existing names, a direct translation to JavaScript of this would be:
bind( bind( unit(3), cube), sine)
Where the bind function now looks like this:
// bind :: (Number,String) -> (Number -> (Number,String)) -> (Number,String)
var bind = function(x, f) { // e.g. x = [3, ''], f = cube
var y = x[0], // 3
s = x[1], // ''
fy = f(y), // cube(3) = [27, 'cube was called.']
z = fy[0], // 27
t = fy[1]; // 'cube was called.'
return [z, s + t];
};
This representation is clearer, but not quite as expressive as the Haskell version. Let’s go one step forward:
var y = pipe(unit(3), [cube, sine])
This seems reasonably close to Haskell’s version, and to go any further we’d really need some syntactic abstractions that JavaScript does not have. This pipe function is straightforward to implement: it takes a monadic value, and uses bind to pipe it through the list of debuggable functions:
// pipe :: (Number,String) -> [Number -> (Number,String)] -> (Number,String)
var pipe = function(x, functions) {
for (var i = 0, n = functions.length; i < n; i++) {
x = bind(x, functions[i]);
}
return x;
};
We can easily use this with anonymous inline functions without losing much expressiveness:
var z = pipe(unit(7), [ function(x) { return [x+1, 'inc.'] },
function(x) { return [2*x, 'double.'] },
function(x) { return [x-1, 'dec.'] }
])
// z == [15, 'inc.double.dec.']
Here we’ve calculated (2 * (7 + 1)) - 1 using fairly direct syntax, where the operations and the log messages are nicely separated. If you’ve done a lot of asynchronous programming, this will probably be starting to look somewhat familiar, and indeed I’ll be showing where this leads in the next article.
Seems like function chaining (as in jQuery) is the most idiomatic Javascript approach to pipelining. It’s not syntactically identical with what you’re thinking about but i think it’s the closest thing in terms of approach.
Method chaining is one approach, and I can see why you’d think it’s an obvious solution here. I realize this is a contrived example, but I’m just trying to use something simple to demonstrate a pattern that can be used to solve much more complex problems. I’ll try to expand on this in my next post.
I think you need to s/3/x/ a couple times starting at the line: “bind(sine)(bind(cube)(unit(3)))”.
I don’t know if it’s well supported across browsers, but there is this pretty cool Lambda-ish syntax that exists:
https://developer.mozilla.org/en/New_in_JavaScript_1.8#Expression_closures_%28Merge_into_own_page.2fsection%29
Developing In Javascript | Aquilent Blog
can you more posts regarding computations in javascript.
Sintaxis de mónadas para JavaScript
Las Promesas son las mónadas de la programación asíncrona
Monad Monad Monad … | 따라쟁이
for the version of bind you use in
bind( bind( unit(3), cube), sine)
your documentation says:
// bind :: (Number,String) -> (Number -> (Number,String)) -> (Number,String)
I think it should be
// bind :: ((Number,String),(Number -> (Number,String))) -> (Number,String)