ekmett / adjunctions

Simple adjunctions
http://hackage.haskell.org/package/adjunctions
Other
44 stars 27 forks source link

New Representable methods feel cramped #60

Open treeowl opened 5 years ago

treeowl commented 5 years ago

We now have

  collect1 :: Functor1 w => (forall x. g x -> f x) -> w g -> f (w Identity)

We can generalize this immediately:

  coll2 :: (Applicative h, Functor1 w) => (forall x. g x -> f x) -> w g -> f (w h)
  coll2 f = cotraverse1 (map1 (pure . runIdentity)) . map1 f

I don't know if it's possible to weaken the Applicative constraint.

treeowl commented 5 years ago

Ah, like this:

  coll1
    :: Functor1 w
    => (forall x. x -> h x) -> (forall x. g x -> f x) -> w g -> f (w h)

This can avoid an extra fmap (map1 (p . runIdentity)) at the end, compared to the current collect1.

treeowl commented 5 years ago

No no no, that's not the way to do it either. Here we go:

  collection
     :: Functor1 w
     => (forall x. g x -> f (h x))
     -> w g -> f (w h)
  collection f =
     tabulate
     . (\w i -> map1 (flip index i . getCompose) w)
     . map1 (Compose #. f)

Isn't that more satisfying?

treeowl commented 5 years ago

cotraverse1 then becomes

  cotraverse1' :: Functor1 w => (w h -> a) -> w (Compose f h) -> f a

which is unsatisfying in just the right way. Finally, we get to the most powerful method,

  cotraverseMap1' ::
       Functor1 w => (w h -> a) -> (forall x. g x -> f (h x)) -> w g -> f a
  cotraverseMap1' f g = cotraverse1' f . map1 (Compose . g)

Lovely, eh?

aaronvargo commented 5 years ago

Isn't that more satisfying?

The point of the new methods is to avoid index since it's O(n) for instances like Stream.

cotraverse1 then becomes

@sjoerdvisscher also suggested that version on twitter. As I replied there, I had that version at one point but found that the Identity version is more convenient. The compose version should also be added though, and could replace a bit of code duplication in the Cofree and (:.:) instances. I can make a PR for this and a couple other additions I've had in mind.

I don't think the compose version needs to be a method though; it can just be a derived function.

treeowl commented 5 years ago

No comment on that particular practicality, but I've run into another rather annoying one: the cotraverse1 and distribute1 methods are incompatible with GeneralizedNewtypeDeriving. Ouch! This isn't really surprising, since distribute is as well. But that makes me very tempted to say we should omit those and just have collect1 and cotraverseMap1.