{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
module Data.Profunctor where
import Prelude hiding ((.), id)
import Control.Arrow (Kleisli (..), (|||))
import Control.Category
import Control.Comonad
import Control.Monad
import Control.Monad.Fix
import Data.Cotraversable
class Profunctor p where
dimap :: (a -> b) -> (c -> d) -> p b c -> p a d
dimap f g = lmap f . rmap g
lmap :: (a -> b) -> p b c -> p a c
lmap f = dimap f id
rmap :: (b -> c) -> p a b -> p a c
rmap g = dimap id g
infixr 1 ^>>, >>^, <<^, ^<<
(^>>) :: Profunctor p => (a -> b) -> p b c -> p a c
(^>>) = lmap
(>>^) :: Profunctor p => p a b -> (b -> c) -> p a c
(>>^) = flip rmap
(<<^) :: Profunctor p => p b c -> (a -> b) -> p a c
(<<^) = flip lmap
(^<<) :: Profunctor p => (b -> c) -> p a b -> p a c
(^<<) = rmap
instance Profunctor (->) where
dimap f g a = g . a . f
instance Functor f => Profunctor (Kleisli f) where
dimap f g (Kleisli a) = Kleisli (fmap g . a . f)
instance Functor f => Profunctor (Cokleisli f) where
dimap f g (Cokleisli a) = Cokleisli (g . a . fmap f)
class Profunctor p => Related f p where
relate :: p a b -> p (f a) (f b)
instance Functor f => Related f (->) where relate = fmap
instance (Applicative p, Traversable f) => Related f (Kleisli p) where
relate = Kleisli . traverse . runKleisli
instance (Cotraversable f, Functor ɯ) => Related f (Cokleisli ɯ) where
relate = Cokleisli . cotraverse . runCokleisli
instance {-# OVERLAPPING #-} Comonad ɯ => Related (Either a) (Cokleisli ɯ) where
relate (Cokleisli f) =
(\ a -> Left . copure . (a <$)) |||
(\ a -> Right . f . (a <$)) ^>> Cokleisli (copure <*> void)
class Profunctor p => Corelated f p where
corelate :: p (f a) (f b) -> p a b
instance Corelated ((,) a) (->) where
corelate f a = let (c, b) = f (c, a) in b
instance MonadFix m => Corelated ((,) a) (Kleisli m) where
corelate (Kleisli f) = Kleisli $ \ a -> snd <$> mfix (f . flip (,) a . fst)
instance Corelated (Either a) (->) where
corelate f = let go = either (go . f . Left) id in go . f . Right
instance Monad m => Corelated (Either a) (Kleisli m) where
corelate (Kleisli f) = let go = either (go <=< f . Left) pure in Kleisli (go <=< f . Right)
instance Functor f => Corelated (Either a) (Cokleisli f) where
corelate (Cokleisli f) = Cokleisli (go . fmap Right)
where go ɯ = case f ɯ of Right b -> b
Left c -> go (Left c <$ ɯ)