Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

eamsden
Copy link

@eamsden eamsden commented Oct 20, 2016

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
Copy link
Author

eamsden commented Oct 20, 2016

This implementation is wrong. Sorry.

@eamsden eamsden closed this Oct 20, 2016
@eamsden eamsden reopened this Oct 20, 2016
@eamsden
Copy link
Author

eamsden commented Oct 20, 2016

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

@Gabriella439
Copy link
Owner

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
Copy link
Collaborator

chris-martin commented Apr 23, 2021

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

  • It is useful for simple testing purposes.
  • It provides a place for the documentation explaining that it is not idiomatic.

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:

  • The haddock on ListT might emphasize that use of this type very often involves using Select and enumerate to convert back and forth with Producer, and that when you're perusing the API for ways to construct a ListT value and for things that you can do with a ListT value, you should not forget to consider all the Producer utilities in Pipes.Prelude.
  • The haddock on runListT might emphasize that it discards all the values, suggest that if you need to "get" the values you should apply enumerate and look at the "Folds" section in Pipes.Prelude, and directly address the example of (toListM . enumerate) and discuss why that sacrifices streaming.
  • Pipes.Tutorial might benefit from the addition of a section about folds, which would be another place to discuss the situations when folding a Producer is or is not appropriate, and another place to mention that if you want to fold a ListT, the first thing you're going to want to do is apply enumerate and then from there think about how you want to fold the Producer using an appropriate Pipes.Prelude function.
  • On a different but related note, I think (Select . each) is another idiom that ought to be highlighted more. Pipes.Tutorial does mention this one, but perhaps it should also appear in the haddock somewhere in the Pipes module.

@Gabriella439
Copy link
Owner

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

@chris-martin
Copy link
Collaborator

Lovely! I intend to.

chris-martin added a commit to chris-martin/Haskell-Pipes-Library that referenced this pull request May 1, 2021
This implements several of the documentation ideas I brought up in Gabriella439#184.

The present problem, I think, is that it is not obvious how to accomplish
some basic ListT tasks (e.g. converting `ListT m a` to `m [a]`) unless
you have the habit of knowing to convert to Producer first.

I also wanted to emphasize some of the things that you can do using just
the typeclass instances.

I'm aiming to augment the ListT API documentation with just enough links
and suggestions to serve as jumping-off points, without cluttering up
the haddock page for the Pipes module too much.
Gabriella439 pushed a commit that referenced this pull request May 3, 2021
This implements several of the documentation ideas I brought up in #184.

The present problem, I think, is that it is not obvious how to accomplish
some basic ListT tasks (e.g. converting `ListT m a` to `m [a]`) unless
you have the habit of knowing to convert to Producer first.

I also wanted to emphasize some of the things that you can do using just
the typeclass instances.

I'm aiming to augment the ListT API documentation with just enough links
and suggestions to serve as jumping-off points, without cluttering up
the haddock page for the Pipes module too much.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants