A function const id = x => x
becomes a continuation passing style function in Javascript as soon as you add a function argument and call it with the result value in tail position: const id_ = x => k => k(x)
. The actual continuation emerges when you apply the CPS function: id_(2)
yields const id2 = k => k(2)
, the continuation.
You can compose normal functions with const comp = f => g => x => f(g(x))
and then comp(id) (id)
. But can you compose continuations like id2
as well?
// doesn't work, cont2 returns a value but cont expects a fun
const comp_ = cont => cont2 => k => cont(cont2(k));
// works but x is awkwardly discarded
const comp_ = cont => cont2 => k => cont2(x => cont(y => k(y));
Unfortunately, no. There is no specialized comp_
operator for id2
to compose two continuations in a general way. But there is a way to compose the CPS version id_
with its continuation id2
:
const comp_ = cont => cps => k => cont(x => cps(x) (k));
const f = comp_(id2) (id_);
f(id); // yields 2 since we are only composing identity
comp_
a.k.a. chain
/bind
is one part of the monad interface. The second one is const of = x => k => k(x)
, which simply puts a normal value into the monad context:
const chain = cont => cps => k => cont(x => cps(x) (k));
const of = x => k => k(x);
const f = chain(of(3)) (id_);
f(id); // yields 3
So not just the continuation monad but monads of all types are just the most general way to compose/chain expressions of those types using continuations under the hood. Please note that monads are a very generalized and thus subtle concept. Stare at it long enough, play with it once in a while and you'll eventually get it.
Top comments (0)