content: {-# 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)))
  