question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Why FL specifies the ChainRec typeclass when there is the trampoline monad?

See original GitHub issue

Here is an example of a computation, which is both monadic and recursive. It either yields a result or short circuits and yields nothing, if a single element of the array is nothing.

const maybeSum = xs => {
  const go = (tx, i) =>
    i === xs.length
      ? tx
      : optChain(tx) (acc =>
          optChain(xs[i]) (x => go(optOf(acc + x), i + 1)))

  return go(optOf(0), 0);
};

maybeSum is not stack safe. The recursive step go is in tail position, so we might be inclined to apply a normal trampoline. However, such a trampoline would break the short circuit semantics of the Option monad (runnable example).

AFAIK, making such a computation stack safe only requires a monad, not a new typeclass. Here is the monad instance of the trampoline type:

const monadRec = step => {
    while (step.tag !== Base)
      step = step.f(...step.args);

    return step.x;
};

const Base = x =>
  ({tag: Base, x});

Chain = f => (...args) =>
  ({tag: Chain, f, args});

const recChain = tf => fm =>
  tf.tag === Chain
    ? Chain(args => recChain(tf.f(...args)) (fm)) (tf.args)
    : fm(tf.x);

const recOf = Base;

Given this monad instance we can implement sum using the Option monad transformer:

const optChainT = ({chain, of}) => mmx => fmm =>
  chain(mmx)
    (mx =>
      match(mx, {
        None: _ => of(None),
        Some: ({some: x}) => fmm(x)
      }));

const optOfT = of => x => of(Some(x));

const optRecChain = optChainT({chain: recChain, of: recOf});

const optRecOf = optOfT(recOf);

const sum = xs => {
  const go =
    Chain((tx, i) =>
      i === xs.length
        ? tx
        : optRecChain(tx) (acc =>
            optRecChain(recOf(xs[i])) (x => go(optRecOf(acc + x), i + 1))));

  return go(optRecOf(0), 0);
};

All we need to do is to add the trampoline monad as the innermost monad of our transformer stack. Here is an runnable example.

So what is the motivation behind the ChainRec typeclass when all we need seems to be a new monad instance. Please note that I don’t want to imply that there is no good reason for ChainRec. I just cannot see it right now, hence the question. Thank you!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
davidchamberscommented, Mar 16, 2020

I think it’s a good question. I’ve always found ChainRec confusing, though, so I’m no help. 😉

0reactions
ivenmarquardtcommented, Jul 11, 2020

I wonder why PS cannot optimize recursion within a monad when we can write a stack-safe monad instance for the Trampoline type in a principled way. I am not a compiler programmer though.

With Trampoline you can add stack-safety to many transfromer stacks and keep your code DRY. ChainRec on the other hand is probably more efficient. I will use the monad instance for the time being and report here if I ever feel the need for ChainRec.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Add ChainRec type class · Issue #151 · fantasyland/fantasy-land
TailRec - A type class which captures stack-safe monadic tail recursion. It would be nice to have a common specification for Tail Recursive ......
Read more >
scala - Trampoline as a Functor - Stack Overflow
I understand the classic implementation of the Trampoline as a Monad as described in Stackless Scala. However, is there a way to implement...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found