purescript / purescript-free

Free monads, Cofree comonads, Yoneda and Coyoneda functors, and the Trampoline monad.
BSD 3-Clause "New" or "Revised" License
93 stars 27 forks source link

Foldable, etc. instances for Coyoneda/Yoneda #95

Closed LiamGoodacre closed 6 years ago

garyb commented 6 years ago

Are these what you had in mind?

instance foldableCoyoneda :: (Functor f, Foldable f) => Foldable (Coyoneda f) where
  foldr f z = foldr f z <<< lowerCoyoneda
  foldl f z = foldl f z <<< lowerCoyoneda
  foldMap f = foldMap f <<< lowerCoyoneda

instance traversableCoyoneda :: Traversable f => Traversable (Coyoneda f) where
  traverse f = map liftCoyoneda <<< traverse f <<< lowerCoyoneda
  sequence = map liftCoyoneda <<< sequence <<< lowerCoyoneda

instance foldable1Coyoneda :: (Functor f, Foldable1 f) => Foldable1 (Coyoneda f) where
  foldMap1 f = foldMap1 f <<< lowerCoyoneda
  fold1 = fold1 <<< lowerCoyoneda

instance traversable1Coyoneda :: Traversable1 f => Traversable1 (Coyoneda f) where
  traverse1 f = map liftCoyoneda <<< traverse1 f <<< lowerCoyoneda
  sequence1 = map liftCoyoneda <<< sequence1 <<< lowerCoyoneda

I couldn't see an obvious way of doing without the Functor constraints, but that does limit the usefulness somewhat, unless this is just for fusion or something like that.

LiamGoodacre commented 6 years ago

More like:

foldMap f = unCoyoneda \g -> foldMap (f <<< g)

See all the instances here https://hackage.haskell.org/package/kan-extensions-5.1/docs/Data-Functor-Coyoneda.html with source: https://hackage.haskell.org/package/kan-extensions-5.1/docs/src/Data-Functor-Coyoneda.html#line-201

LiamGoodacre commented 6 years ago

It means we can do things like:

import Prelude
import Data.Monoid
import Data.Foldable

newtype Coyo f a = Coyo (forall r. (forall b. (b -> a) -> f b -> r) -> r)

coyo :: forall a b f. (b -> a) -> f b -> Coyo f a
coyo f m = Coyo \c -> c f m

instance foldableCoyo :: Foldable f => Foldable (Coyo f) where
  foldMap g (Coyo c) = c \t -> foldMap (g <<< t)
  foldr m = foldrDefault m
  foldl m = foldlDefault m

intercalateMap :: forall a m f. Foldable f => Monoid m => (a -> m) -> m -> f a -> m
intercalateMap s z m = intercalate z (coyo s m)

Gives you a way of taking something restricted to f m -> m and turning it into (a -> m) -> f a -> m without needing Functor f.