DEV Community

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

Collapse
 
ryanleecode profile image
Ryan Lee

I'm sorry I don't understand the question. Can you rephrase it?

Collapse
 
iquardt profile image
Iven Marquardt • Edited

The evaluation of the following method chain is prematurely ended in A:

Do(option)
    .bind("a", some(5))
    .bind("b", none) // A
    .bind("c", some(6))
    .return(({a, b, c}) => a + b + c)
Enter fullscreen mode Exit fullscreen mode

You can easily express such control flows with nested computations but regularly not with sequential ones. Do you know how they solved this issue? Do they really break out of the chain or have to go through the entire structure? The latter would be rather inefficient.

Thread Thread
 
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