Allow multiple collections in `for-yield`
@mlutze @jaschdoc Question is: how?
Could a MonadZip class help?
This was added to Haskell for monad comprehensions.
Could a
MonadZipclass help?This was added to Haskell for monad comprehensions.
Do you have a link?
George Giorgidze, et al - Bringing Back Monad Comprehensions
https://db.inf.uni-tuebingen.de/staticfiles/publications/haskell2011.pdf
There is also Jeremy Gibbons's - Comprehending Ringads
https://www.cs.ox.ac.uk/jeremy.gibbons/publications/ringads.pdf
I've implemented Haskell's MonadZip class in Flix - shall I make a PR to add it to the stdlib?
I've implemented Haskell's
MonadZipclass in Flix - shall I make a PR to add it to the stdlib?
Yes, please! I cannot guarantee we will use it, but I can take a look and try to understand it :)
Paper looks interesting. Just need to find the time :)
I've created a PR #4347 - feat: add a MonadZip class, potentially helping issue #4230
I'm looking at adding some more Applicative / Monad traversals to the stdlib so I've included a generic zipWithA function that isn't in the Haskell class.
I am still intrigued by this, I just have not gotten around to it yet.
I read both papers with great enjoyment. I think a lot of the stuff is a bit too advanced to add to Flix (at least for the moment), but I was happy to see that we are pretty close to where Haskell is at. (I did find one improvement we can make, reflected in https://github.com/flix/flix/issues/4429).
I think we can add MonadZip and syntax for it, e.g. (for (x <- xs | y <- ys), but I am less convinced of whether it would be worth it. (Perhaps we can add the type class, but no comprehension syntax). In any case, none of these papers seems to suggest to me a way to get the flexibility that Scala has, but possible such a system is simply in-expressible in a language like Flix/Haskell.
All this to say, I am not sure exactly what should be "next" for the for-yield/comprehension syntax. Possibly we just leave it where it is at the moment.
I think this can be done using Iterable along with a type class like below. Materializable.materialize would be wrapped around the entire expression in the weeder (along with a region so it remains pure). That way we're just manipulating / appending iterators which finally materialize to the type we expect.
This is partly inspired by Scala's way of handling it via inheritance and just defaulting to Iterable most of the time.
The downside is that we completely abandon the monad implementation.
pub class Materializable[t : Type -> Type] {
pub def materialize(iter: Iterator[a, r]): t[a] \ Read(r)
}
instance Materializable[List] {
pub def materialize(iter: Iterator[a, r]): List[a] \ Read(r) =
Iterator.toList(iter)
}
We now have foreach-yield which allows this with iterators.