fp-ts-contrib icon indicating copy to clipboard operation
fp-ts-contrib copied to clipboard

Do with filter (if)

Open Gurimarukin opened this issue 5 years ago • 7 comments

🚀 Feature request

In Scala, you can do a for-comprehension with a if:

for {
  n <- Some(12)
  if n < 10
  m <- Some(n * 2)
} yield m

Would it be possible to do this with a Do?
Maybe with this syntax:

Do(option)
  .bind('n', some(12))
  .filter(({ n }) => n < 10)
  .bindL('m', ({ n }) => some(n * 2))
  .return(({ m }) => m)

In Scala, the for-comprehension checks that the given Monad has a withFilter method.

  • You can't do this for an Either
  • For a Future, the failed value is:
    Future(Failure(NoSuchElementException("Future.filter predicate is not satisfied")))
    
    But I have no idea how this could be done with a Task or a TaskEither. Maybe with a TaskOption?
  • Other Monads?

Gurimarukin avatar Feb 21 '20 09:02 Gurimarukin

This would probably work for monads that are Filterable. Whether or not all Monads are Filterable, I do not know.

https://gcanti.github.io/fp-ts/modules/Filterable.ts.html https://gcanti.github.io/fp-ts/modules/FilterableWithIndex.ts.html

cyberixae avatar Apr 28 '20 12:04 cyberixae

It seems a Monad is not necessarily Filterable. However, you could implement your own For for things that are both Monad and Filterable. It could use ´Do´ for the operations that do not require for the type to be Filterable.

cyberixae avatar Apr 29 '20 15:04 cyberixae

I made an experimental implementation of For notation. However, I can not figure out how to reuse code from the DoClass so I had to use copy/paste to make it work. Other than that it seems to work. See https://github.com/cyberixae/fp-ts-contrib/commits/implement_for_notation

cyberixae avatar May 03 '20 20:05 cyberixae

Oh wow! Nice job! I was thinking about the method's name: wouldn't this be closer to the usual conventions of fp-ts:

For(option)
  .bind('n', some(12))
  .filter(2 > 3)
  .filterL(({ n }) => n < 10)
  .return(({ n }) => n)

But I don't see cases where filter isn't lazy.

Gurimarukin avatar May 04 '20 07:05 Gurimarukin

The L stands for lambda. I think it would be nice to have the constants have a suffix instead of the lambdas since the constant versions are more of a special case. I don't think the L suffix naming is from fp-ts. I'm not sure if it makes sense to add the For notation to fp-ts-contrib. Maybe you could fork it and create your own npm package that implements the for notation. You could then make it more appealing to people with Scala background. Just an idea though. Don't feel obliged to do so.

cyberixae avatar May 04 '20 08:05 cyberixae

I always thought that in fp-ts the L stands for lazy. In v1 of fp-ts, for instance for Option.getOrElse, there used to be both getOrElse et getOrElseL signatures.
https://github.com/gcanti/fp-ts/blob/1.x/docs/modules/Option.ts.md#getorelse-method-1

I have no problem with not adding For to fp-ts-contrib. This was just a suggestion and I managed to make my code work without it :)

Gurimarukin avatar May 04 '20 08:05 Gurimarukin

Perhaps it was used for lazy in fp-ts however the Do notation was developed separately. See https://paulgray.net/do-syntax-in-typescript/#do-l-bind-l-sequence-sl-let-l-variants

cyberixae avatar May 04 '20 09:05 cyberixae