ekmett / bifunctors

Haskell 98 bifunctors, bifoldables and bitraversables
Other
57 stars 42 forks source link

Add unzipWith #119

Open treeowl opened 1 year ago

treeowl commented 1 year ago

We can use traverseBia to implement a lazy unzipWith function for general Traversable instances. While it's trivial in principle, preventing it from leaking memory turns out to be rather tricky, so I think it makes sense to offer it as a function rather than just documenting the possibility.

treeowl commented 1 year ago

If this isn't a good home for this function, I suppose I can give it its own package, but I fear it will be very lonely there.

RyanGlScott commented 1 year ago

Indeed, my only concern with this patch is the fact that it isn't obviously connected to the API that bifunctors offers unless you know unzipWith is implemented under the hood.

One idea (which I'm not sure is good or not, but an idea nonetheless) is to move this to Data.Biapplicative.LazyPair and export the LazyPair data type alongside unzipWith, as LazyPair is more obviously a Biapplicative thing.

treeowl commented 1 year ago

Indeed, my only concern with this patch is the fact that it isn't obviously connected to the API that bifunctors offers unless you know unzipWith is implemented under the hood.

One idea (which I'm not sure is good or not, but an idea nonetheless) is to move this to Data.Biapplicative.LazyPair and export the LazyPair data type alongside unzipWith, as LazyPair is more obviously a Biapplicative thing.

Yes, I agree there's a case to be made for LazyPair—but maybe more than one! This version NOINLINEs a bunch of stuff, which is exactly what you want—sometimes. Not so much other times. And I don't think there's much chance most people would see a module called Data.Biapplicative.LazyPair (or perhaps more likely Data.Bifunctor.LazyPair) and realize that was the place to find an unzipping function. So I'm pretty unsatisfied with all the options I've seen or imagined thus far.

RyanGlScott commented 1 year ago

And I don't think there's much chance most people would see a module called Data.Biapplicative.LazyPair (or perhaps more likely Data.Bifunctor.LazyPair) and realize that was the place to find an unzipping function. So I'm pretty unsatisfied with all the options I've seen or imagined thus far.

Yes, I certainly believe that. There is a similar parallel to Data.Traversable's mapAccumL and mapAccumR functions, which are built on top of state transformers under the hood. It definitely would be out of place to export mapAccumL and mapAccumR from Control.Monad.Trans.State, for instance.

The same reasoning could be applied here: if someone wanted to find an unzipWith function, then bifunctors is an unlikely home for it. The would suggest that unzipWith deserves to live in a separate package, unless you are fine with finding a way to dress it up in bifunctors clothing.

treeowl commented 1 year ago

A separate package has its own entirely nontrivial discovery problems. Can we at least agree on adding two lazy pair types (one with NOINLINEs and one without) and a strict one to bifunctors? Can you come up with names and module names?

RyanGlScott commented 1 year ago

Can we at least agree on adding two lazy pair types (one with NOINLINEs and one without) and a strict one to bifunctors?

Sure, I would be fine with that. As far as names go, do you have any objections to LazyPair? I would be tempted to put these in modules named Data.Bifunctor.LazyPair and Data.Bifunctor.LazyPair.NoInline, each exposing the same API.

treeowl commented 1 year ago

I'm a bit nervous about what people might make of the latter name, but I don't have a great alternative. The naming on the strict side is hard too; when I think of a "strict pair" I think of one with strict fields. Maybe NonLazy? Blech.