{-# LANGUAGE EmptyCase #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE LinearTypes #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE NoImplicitPrelude #-} -- | This module provides (linear) Kleisli and CoKleisli arrows -- -- This module is meant to be imported qualified, perhaps as below. -- -- > import qualified Data.Profunctor.Kleisli as Linear -- -- == What are Kleisli arrows? -- -- The basic idea is that a Kleisli arrow is like a function arrow -- and @Kleisli m a b@ is similar to a function from @a@ to @b@. Basically: -- -- > type Kleisli m a b = a %1-> m b -- -- == Why make this definition? -- -- It let's us view @Kleisli m@ for a certain @m@ as a certain kind of -- function arrow, give it instances, abstract over it an so on. -- -- For instance, if @m@ is any functor, @Kleisli m@ is a @Profunctor@. -- -- == CoKleisli -- -- A CoKleisli arrow is just one that represents a computation from -- a @m a@ to an @a@ via a linear arrow. (It's a Co-something because it -- reverses the order of the function arrows in the something.) module Data.Profunctor.Kleisli.Linear ( Kleisli (..), CoKleisli (..), ) where import qualified Control.Functor.Linear as Control import qualified Data.Functor.Linear as Data import Data.Profunctor.Linear import Data.Void import Prelude.Linear (Either (..), either) import Prelude.Linear.Internal -- Ideally, there would only be one Kleisli arrow, parametrised by -- a multiplicity parameter: -- newtype Kleisli p m a b = Kleisli { runKleisli :: a # p -> m b } -- -- Some instances would also still work, eg -- instance Functor p f => Profunctor (Kleisli p f) -- | Linear Kleisli arrows for the monad `m`. These arrows are still useful -- in the case where `m` is not a monad however, and some profunctorial -- properties still hold in this weaker setting. newtype Kleisli m a b = Kleisli {runKleisli :: a %1 -> m b} instance Data.Functor f => Profunctor (Kleisli f) where dimap f g (Kleisli h) = Kleisli (Data.fmap g . h . f) instance Control.Functor f => Strong (,) () (Kleisli f) where first (Kleisli f) = Kleisli (\(a, b) -> (,b) Control.<$> f a) second (Kleisli g) = Kleisli (\(a, b) -> (a,) Control.<$> g b) instance Control.Applicative f => Strong Either Void (Kleisli f) where first (Kleisli f) = Kleisli (either (Data.fmap Left . f) (Control.pure . Right)) second (Kleisli g) = Kleisli (either (Control.pure . Left) (Data.fmap Right . g)) instance Data.Applicative f => Monoidal (,) () (Kleisli f) where Kleisli f *** Kleisli g = Kleisli $ \(x, y) -> (,) Data.<$> f x Data.<*> g y unit = Kleisli $ \() -> Data.pure () instance Data.Functor f => Monoidal Either Void (Kleisli f) where Kleisli f *** Kleisli g = Kleisli $ \case Left a -> Left Data.<$> f a Right b -> Right Data.<$> g b unit = Kleisli $ \case {} instance Control.Applicative f => Wandering (Kleisli f) where wander traverse (Kleisli f) = Kleisli (traverse f) -- | Linear co-Kleisli arrows for the comonad `w`. These arrows are still -- useful in the case where `w` is not a comonad however, and some -- profunctorial properties still hold in this weaker setting. -- However stronger requirements on `f` are needed for profunctorial -- strength, so we have fewer instances. newtype CoKleisli w a b = CoKleisli {runCoKleisli :: w a %1 -> b} instance Data.Functor f => Profunctor (CoKleisli f) where dimap f g (CoKleisli h) = CoKleisli (g . h . Data.fmap f) instance Strong Either Void (CoKleisli (Data.Const x)) where first (CoKleisli f) = CoKleisli (\(Data.Const x) -> Left (f (Data.Const x)))