-
Notifications
You must be signed in to change notification settings - Fork 378
Description
TailRec - A type class which captures stack-safe monadic tail recursion.
It would be nice to have a common specification for Tail Recursive Monads in JS community, so that libraries like Free could use the interface to be stack safe.
in PS MonadRec
type class looks like this:
class Monad m <= MonadRec m where
tailRecM :: forall a b. (a -> m (Either a b)) -> a -> m b
So we could have something like this without stack overflow:
Identity.tailRecM((n) => {
if (n == 0) {
return Identity(Either.Right("DONE"))
}
return Identity(Either.Left(n - 1))
})(20000)
one thing is that there are multiple Either
implementations in js and this spec should depend on bare minimum from any Either
type. from what I have found the minimum api from Either type is to have a cata
method. but before that let's see an implementation of tailRecM
of Identity
:
const tailRec = (f) => (a) => {
let v = f(a)
while (!isDone(v)) {
v = f(getValue(v))
}
return getValue(v)
}
const runIdentity = (i) => i.x
Identity.tailRecM = (f) => (a) => Identity(tailRec((v) => runIdentity(f(v)))(a))
So we have seen usage of it and part of it's implementation. now what we have left is implement isDone
and getValue
so that they are as generic as possible.
const isDone = (e) => e.cata({
Right: () => true,
Left: () => false,
})
const getValue = (e) => e.cata({
Right: (v) => v,
Left: (v) => v,
})
so as it looks any Either with cata
would work so users shouldn't be tied to some particular Either implementation (as long as it has cata
).
To note this Either implementation would work with tailRecM
.
const Either = {
Right: (v) => ({ cata: (c) => c.Right(v) }),
Left: (v) => ({ cata: (c) => c.Left(v) }),
}
The hardest was to implement tailRecM
for Task/Future
but i have made it and after we agree on some interface I would create PRs for some popular Task/Future
implementations