ekmett / profunctors

Haskell 98 Profunctors
http://hackage.haskell.org/package/profunctors
Other
70 stars 43 forks source link

Extract `wander`/`traverse'` as separate function #112

Closed mniip closed 2 months ago

mniip commented 4 months ago

The library has a Traversing typeclass with two methods:

traverse' :: forall a b g. Traversable f => p a b -> p (f a) (f b)
wander :: forall s t a b. Traversal s t a b -> p a b -> p s t

These two methods have default implementations in terms of each other doing some rather non-trivial manipulations with Bazaar-the-Applicative and Bazaar-the-Traversable.

However these manipulations are rather useful in other contexts, for concrete a, b, and p which need not be Traversing itself, e.g. defining these two functions in terms of each other:

bulk :: forall f. Traversable f => P (f A) (f B)
bulkOf :: forall s t. Traversal s t A B -> P s t

One way is straightforward:

bulk = bulkOf traverse

To define bulkOf in terms of bulk we need an "inverse" of ($ traverse):

generalizeTraversal :: Profunctor p => p (Baz t b a) (Baz t b b) -> Traversal s t a b -> p s t
generalizeTraversal lift traversal = dimap (\s -> Baz $ flip traversal s) sold lift

Then we have:

bulkOf = generalizeTraversal bulk

wander t pab = generalizeTraversal (traverse' pab) t

generalizeTraversal . ($ traverse) = id
($ traverse) . generalizeTraversal = id

Some things up to discussion:

mniip commented 4 months ago

After some thought I realized the function can actually be extracted from existing profunctors code like so:

generalizeTraversal
  :: Profunctor p
  => (forall f. Traversable f => p (f a) (f b)) -> Traversal s t a b -> p s t
generalizeTraversal p trav = dimap Identity runIdentity
  $ runCofreeTraversing $ wander trav $ CofreeTraversing p