Gabriella439 / pipes

Compositional pipelines
BSD 3-Clause "New" or "Revised" License
487 stars 72 forks source link

Add evalListT to get results from ListT as a list #184

Open eamsden opened 7 years ago

eamsden commented 7 years ago

The proper way to use ListT in pipes is to have an underlying monad and effect per choice through that monad. However, sometimes it's really convenient to run a ListT computation and then get the results in the underlying monad as a list. Since the MTL's ListT is more restrictive, many use cases for this functionality would be helped by using pipes' ListT, while still obtaining the results as a list.

eamsden commented 7 years ago

This implementation is wrong. Sorry.

eamsden commented 7 years ago

New commit fixes the implementation and it now works as expected.

Gabriella439 commented 7 years ago

A simpler implementation is to define this in terms of Pipes.Prelude.toListM by taking advantage of the fact that ListT is just a newtype wrapper around Producer. I think it would be something like this:

evalListT :: Monad m => ListT m a -> m [a]
evalListT = Pipes.Prelude.toListM . Pipes.enumerate

I would also suggest naming this something like forceListT to convey to the user that this will not run in constant space

chris-martin commented 3 years ago

I found myself wishing for this function, for the same reasons that I appreciate Pipes.Prelude.toListM:

I considered naming it collectListT, because I think "collect" conveys the warning that all the values get collected at once, thus losing the streaming behavior. This is a word that Java uses for something similar, and I think its meaning is clear in that context.

It can indeed be defined very nicely as toListM . enumerate. This forces the function to be located in Pipes.Prelude instead of Pipes, since toListM comes from the Pipes.Prelude module, which imports Pipes, not the other way around. That seems reasonable enough, because this function probably is not enough of a core idea to go into the library's central module.

So then I started thinking about how this would fit into Pipes.Prelude, and I have to conclude that it really doesn't. Mostly because I cannot figure out how to justify why only toListM should have a ListT variant while all the rest of the functions in the "Folds" section -- fold, fold', foldM, foldM', all, any, and, or, elem, notElem, find, findIndex, head, index, last, length, maximum, minimum, null, sum, product, toList, toListM, toListM' -- should not.

Having talked myself out of wanting this function, I'm now of the opinion that there are a handful of documentation opportunities here instead:

Gabriella439 commented 3 years ago

@chris-martin: If you put up a pull request, I'd accept it

chris-martin commented 3 years ago

Lovely! I intend to.