DEV Community

Discussion on: Practical Guide to Fp-ts P6: The Do Notation

 
ryanleecode profile image
Ryan Lee • Edited

The snippets below are functionally equivalent. It will stop at b because its none. I'm not sure if I understand the question yet. At the end of the day its just a series of chains/flatmaps and it will "break out" on the first one that fails.

Do(option)
  .bind('a', some(5))
  .bind('b', none as Option<number>) // cast otherwise it will default to Option<never>
  .bind('c', some(6))
  .return(({ a, b, c }) => a + b + c)
Enter fullscreen mode Exit fullscreen mode

option.chain(some(5), (a) =>
  option.chain(none as Option<number>, (b) =>
    option.map(some(6), (c) => a + b + c),
  ),
)
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
iquardt profile image
Iven Marquardt • Edited

I don't think you can call both computations equivalent, because the former has a sequential control flow, whereas the latter has a nested one. Do/bind needs to conduct some additional plumbing to allow "short circution" as the nested chain does. And that is exactly my question. How do they do it? With a promise chain I can short circuit by invoking the error callbac, but this only works for the promise type. With Do , however, short circution needs to work for various monads.

Thread Thread
 
ryanleecode profile image
Ryan Lee

Maybe this is what you're talking about?

github.com/gcanti/fp-ts-contrib/bl...

The extra plumbing is just a map operation to keep variables in context. If you want to do it manually here's what it does under the hood.

pipe(
  O.some(5),
  O.chain((a) =>
    pipe(
      O.none as O.Option<number>,
      O.map((b) => ({ a, b })),
    ),
  ),
  O.chain(({ a, b }) =>
    pipe(
      O.some(6),
      O.map((c) => a + b + c),
    ),
  ),
)

Enter fullscreen mode Exit fullscreen mode