packages feed

proarrow-0.1.0.0: src/Proarrow/Profunctor/Instance/Arrow.hs

{-# OPTIONS_GHC -Wno-orphans #-}

-- | Classic "Control.Arrow" arrows as promonads: 'Arr' wraps any 'Control.Arrow.Arrow' as a 'Promonad',
-- with the strength and monoidal instances corresponding to the arrow's capabilities
-- ('Control.Arrow.ArrowChoice', 'Control.Arrow.ArrowLoop', 'Control.Arrow.ArrowApply', ...).
module Proarrow.Profunctor.Instance.Arrow where

import Control.Arrow
  ( Arrow (..)
  , ArrowApply (..)
  , ArrowChoice (..)
  , ArrowLoop (..)
  , Kleisli (..)
  , (>>>)
  )
import Control.Category qualified as P
import Control.Monad (MonadPlus)
import Control.Monad.Fix (MonadFix)
import Data.Kind (Type)
import Prelude (Either (..), Functor (..), Monad (..))

import Proarrow.Category.Monoidal (MonoidalProfunctor (..), Tensor)
import Proarrow.Category.Monoidal.Action (CoprodAction)
import Proarrow.Category.Monoidal.Distributive (DistributiveProfunctor)
import Proarrow.Category.Monoidal.Strength (Costrong (..), Strong (..))
import Proarrow.Colimit.BinaryCoproduct (Coprod (..), (++))
import Proarrow.Core (CAT, Profunctor (..), Promonad (..), rmap, type (+->))
import Proarrow.Functor (FromProfunctor (..))
import Proarrow.Limit.BinaryProduct ()
import Proarrow.Profunctor.Representable (Representable (..))

swap :: (b, a) -> (a, b)
swap ~(x, y) = (y, x)

-- | A "Control.Arrow" 'Arrow' wrapped as a 'Promonad' on 'Type'.
type Arr :: CAT Type -> CAT Type
newtype Arr arr a b = Arr {unArr :: arr a b}

instance (Arrow arr) => Profunctor (Arr arr) where
  dimap l r (Arr a) = Arr (arr l >>> a >>> arr r)

instance (Arrow arr) => Promonad (Arr arr) where
  id = Arr (arr id)
  Arr f . Arr g = Arr (g >>> f)

instance (Arrow arr) => Strong Tensor (Arr arr) where
  act (Arr a) = Arr (second a)

instance (ArrowLoop arr) => Costrong Tensor (Arr arr) where
  coact (Arr f) = Arr (loop (arr swap >>> f >>> arr swap))

instance (Arrow arr) => MonoidalProfunctor (Arr arr) where
  one = Arr (arr id)
  Arr l ** Arr r = Arr (l *** r)

instance (ArrowChoice arr) => MonoidalProfunctor (Coprod (Arr arr)) where
  one = Coprod (Arr (arr id))
  Coprod (Arr l) ** Coprod (Arr r) = Coprod (Arr (l +++ r))

instance (ArrowApply arr) => Representable (Arr arr) where
  type Arr arr % a = arr () a
  index (Arr a) b = arr (\() -> b) >>> a
  tabulate f = Arr (arr (\a -> (f a, ())) >>> app)
  repMap f a = a >>> arr f

instance (Functor m) => Profunctor (Kleisli m) where
  dimap l r (Kleisli a) = Kleisli (fmap r . a . l)

instance (Monad m) => Promonad (Kleisli m) where
  id = arr id
  f . g = g >>> f

instance (Monad m) => Strong Tensor (Kleisli m) where
  act = second

instance (MonadPlus m) => Strong CoprodAction (Kleisli m) where
  act (Kleisli a) = Kleisli ((return . Left) ||| (a >>> fmap Right))

instance (MonadFix m) => Costrong Tensor (Kleisli m) where
  coact f = loop (arr swap >>> f >>> arr swap)

instance (Monad m) => MonoidalProfunctor (Kleisli m) where
  one = arr id
  l ** r = l *** r

instance (MonadPlus m) => MonoidalProfunctor (Coprod (Kleisli m)) where
  one = Coprod (Kleisli return)
  Coprod (Kleisli l) ** Coprod (Kleisli r) = Coprod (Kleisli ((l >>> fmap Left) ||| (r >>> fmap Right)))

instance (Functor m) => Representable (Kleisli m) where
  type Kleisli m % a = m a
  index = runKleisli
  tabulate = Kleisli
  repMap = fmap

instance (Promonad p) => P.Category (FromProfunctor p :: Type +-> Type) where
  id = id
  (.) = (.)

instance (MonoidalProfunctor p, Promonad p) => Arrow (FromProfunctor p :: Type +-> Type) where
  arr f = rmap f id
  FromProfunctor f *** FromProfunctor g = FromProfunctor (f ** g)

instance (DistributiveProfunctor p, Promonad p) => ArrowChoice (FromProfunctor p :: Type +-> Type) where
  FromProfunctor f +++ FromProfunctor g = FromProfunctor (f ++ g)

instance (Representable p, MonoidalProfunctor p, Promonad p) => ArrowApply (FromProfunctor p :: Type +-> Type) where
  app = FromProfunctor (tabulate \(FromProfunctor p, b) -> index p b)

instance (Costrong Tensor p, MonoidalProfunctor p, Promonad p) => ArrowLoop (FromProfunctor p :: Type +-> Type) where
  loop (FromProfunctor p) = FromProfunctor (coact @Tensor (dimap swap swap p))