packages feed

catalyst-0.0.0.0: src/Control/Category/Free.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Control.Category.Free where

import qualified Control.Category as C
import Control.Category.Monoidal as C
import Control.Category.Cartesian as C
import Control.Category.Recursive as C
import Data.Kind ( Constraint )

-- Data kinds representing whether each constraint is required.
-- We could use 'Bool', but using separate Data Kinds helps a lot with producing nicer type errors.
data IsCategory = HasCategory | NoCategory
data IsSymmetricProduct = HasSymmetricProduct | NoSymmetricProduct
data IsSymmetricSum = HasSymmetricSum | NoSymmetricSum
data IsMonoidalProduct = HasMonoidalProduct | NoMonoidalProduct
data IsMonoidalSum = HasMonoidalSum | NoMonoidalSum
data IsCartesian = HasCartesian | NoCartesian
data IsCocartesian = HasCocartesian | NoCocartesian
data IsRecursive = HasRecursive | NoRecursive
data IsFixed = HasFixed | NoFixed

data Requirements =
    Req
        IsCategory
        IsSymmetricProduct
        IsSymmetricSum
        IsMonoidalProduct
        IsMonoidalSum
        IsCartesian
        IsCocartesian
        IsRecursive
        IsFixed

type family ConstraintsOf (x :: anykind) (k :: * -> * -> *) = (c :: Constraint) where
  -- The constraints of a 'Requirements' are the constriants of each class-requirement
  ConstraintsOf ('Req cat symP symS monP monS cart cocart rec fix) k = 
      ( ConstraintsOf cat k
      , ConstraintsOf symP k
      , ConstraintsOf symS k
      , ConstraintsOf monP k
      , ConstraintsOf monS k
      , ConstraintsOf cart k
      , ConstraintsOf cocart k
      , ConstraintsOf rec k
      , ConstraintsOf fix k
      )
  -- Each sub-requirement has an associated class
  ConstraintsOf 'HasCategory cat = C.Category cat
  ConstraintsOf 'HasSymmetricProduct cat = C.SymmetricProduct cat
  ConstraintsOf 'HasSymmetricSum cat = C.SymmetricSum cat
  ConstraintsOf 'HasMonoidalProduct cat = C.MonoidalProduct cat
  ConstraintsOf 'HasMonoidalSum cat = C.MonoidalSum cat
  ConstraintsOf 'HasCartesian cat = C.Cartesian cat
  ConstraintsOf 'HasCocartesian cat = C.Cocartesian cat
  ConstraintsOf 'HasRecursive cat = C.Recursive cat
  ConstraintsOf 'HasFixed cat = C.Fixed cat

  ConstraintsOf 'NoCategory cat = ()
  ConstraintsOf 'NoSymmetricProduct cat = ()
  ConstraintsOf 'NoSymmetricSum cat = ()
  ConstraintsOf 'NoMonoidalProduct cat = ()
  ConstraintsOf 'NoMonoidalSum cat = ()
  ConstraintsOf 'NoCartesian cat = ()
  ConstraintsOf 'NoCocartesian cat = ()
  ConstraintsOf 'NoRecursive cat = ()
  ConstraintsOf 'NoFixed cat = ()

type FreeFunction c a b =
    Catalyst
      ('Req
          'HasCategory
          'HasSymmetricProduct
          'HasSymmetricSum
          'HasMonoidalProduct
          'HasMonoidalSum
          'HasCartesian
          'HasCocartesian
          'HasRecursive
          'HasFixed
      ) c

data Catalyst (r :: Requirements) (p :: * -> * -> *) a b where
  ID :: (r ~ 'Req 'HasCategory symP symS monP monS cart cocart rec fix) => Catalyst r p x x
  Comp :: Catalyst ('Req 'HasCategory symP symS monP monS cart cocart rec fix) p x y -> Catalyst ('Req 'HasCategory symP symS monP monS cart cocart rec fix) p y z -> Catalyst ('Req 'HasCategory symP symS monP monS cart cocart rec fix) p x z

  Swap :: (r ~ 'Req 'HasCategory 'HasSymmetricProduct symS monP monS cart cocart rec fix) => Catalyst r p (a, b) (b, a)
  Reassoc :: (r ~ 'Req 'HasCategory 'HasSymmetricProduct symS monP monS cart cocart rec fix) => Catalyst r p (a, (b, c)) ((a, b), c)

  SwapE :: (r ~ 'Req cat symP 'HasSymmetricSum monP monS cart cocart rec fix) => Catalyst r p (Either a b) (Either b a)
  ReassocE :: (r ~ 'Req cat symP 'HasSymmetricSum monP monS cart cocart rec fix) => Catalyst r p (Either a (Either b c)) (Either (Either a b) c)

  First :: (r ~ 'Req cat symP symS 'HasMonoidalProduct monS cart cocart rec fix) => Catalyst r p a b -> Catalyst r p (a, m) (b, m)
  Second :: (r ~ 'Req cat symP symS 'HasMonoidalProduct monS cart cocart rec fix) => Catalyst r p a b -> Catalyst r p (m, a) (m, b)
  -- (***)
  Alongside :: (r ~ 'Req cat symP symS 'HasMonoidalProduct monS cart cocart rec fix) => Catalyst r p a b -> Catalyst r p a' b' -> Catalyst r p (a, a') (b, b')
  -- (&&&)
  Fanout :: (r ~ 'Req cat symP symS monP monS 'HasCartesian cocart rec fix) => Catalyst r p a b -> Catalyst r p a b' -> Catalyst r p a (b, b')

  Left' :: (r ~ 'Req cat symP symS monP 'HasMonoidalSum cart cocart rec fix) => Catalyst r p a b -> Catalyst r p (Either a x) (Either b x)
  Right' :: (r ~ 'Req cat symP symS monP 'HasMonoidalSum cart cocart rec fix) => Catalyst r p a b -> Catalyst r p (Either x a) (Either x b)
  -- (+++)
  EitherOf :: (r ~ 'Req cat symP symS monP 'HasMonoidalSum cart cocart rec fix) => Catalyst r p a b -> Catalyst r p a' b' -> Catalyst r p (Either a a') (Either b b')
  -- (|||)
  Fanin :: (r ~ 'Req cat symP symS monP monS cart 'HasCocartesian rec fix) => Catalyst r p a b -> Catalyst r p a' b -> Catalyst r p (Either a a') b

  Copy :: (r ~ 'Req cat symP symS monP monS 'HasCartesian cocart rec fix) => Catalyst r p x (x, x)
  Consume :: (r ~ 'Req cat symP symS monP monS 'HasCartesian cocart rec fix) => Catalyst r p x ()
  Fst :: (r ~ 'Req cat symP symS monP monS 'HasCartesian cocart rec fix) => Catalyst r p (a, b) a
  Snd :: (r ~ 'Req cat symP symS monP monS 'HasCartesian cocart rec fix) => Catalyst r p (a, b) b

  InjectL :: (r ~ 'Req cat symP symS monP monS cart 'HasCocartesian rec fix) => Catalyst r p a (Either a b)
  InjectR :: (r ~ 'Req cat symP symS monP monS cart 'HasCocartesian rec fix) => Catalyst r p b (Either a b)
  Unify :: (r ~ 'Req cat symP symS monP monS cart 'HasCocartesian rec fix) => Catalyst r p (Either a a) a
  Tag :: (r ~ 'Req cat symP symS monP monS cart 'HasCocartesian rec fix) => Catalyst r p (Bool, a) (Either a a)

  RecurseL :: (r ~ 'Req cat symP symS monP monS cart cocart 'HasRecursive fix) => Catalyst r p (Either a d) (Either b d) -> Catalyst r p a b
  RecurseR :: (r ~ 'Req cat symP symS monP monS cart cocart 'HasRecursive fix) => Catalyst r p (Either d a) (Either d b) -> Catalyst r p a b

  FixL :: (r ~ 'Req cat symP symS monP monS cart cocart rec 'HasFixed) => Catalyst r p (a, d) (b, d) -> Catalyst r p a b
  FixR :: (r ~ 'Req cat symP symS monP monS cart cocart rec 'HasFixed) => Catalyst r p (d, a) (d, b) -> Catalyst r p a b

  LiftC :: p a b -> Catalyst r p a b

instance (forall x y. Show (c x y)) => Show (Catalyst r c a b) where
  show
    = \case
        Fst -> "fst"
        Snd -> "snd"
        Copy -> "copy"
        Consume -> "consume"
        Swap -> "swap"
        Reassoc -> "reassoc"
        SwapE -> "swapE"
        ReassocE -> "reassocE"

        InjectL -> "injectL"
        InjectR -> "injectR"
        Unify -> "unify"
        Tag -> "tag"
        (First l) -> "(first' " <> show l <> ")"
        (Second l) -> "(second' " <> show l <> ")"
        (Alongside l r) -> "(" <> show l <> " *** " <> show r <>  ")"
        (Fanout l r) -> "(" <> show l <> " &&& " <> show r  <> ")"
        (Left' l) -> "(left " <> show l <> ")"
        (Right' r) -> "(right " <> show r <> ")"
        (EitherOf l r) -> "(" <> show l <> " +++ " <> show r <> ")"
        (Fanin l r) -> "(" <> show l <> " +++ " <> show r <> ")"
        (LiftC cab) -> show cab
        ID -> "id"
        (Comp l r) -> "(" <> show l <> " >>> " <> show r <> ")"
        (RecurseL l) -> "(recurseR " <> show l <> ")"
        (RecurseR r) -> "(recurseL " <> show r <> ")"
        (FixL l) -> "(fixL " <> show l <> ")"
        (FixR r) -> "(fixR " <> show r <> ")"

runFree :: forall r p c a b. (ConstraintsOf r p) => (forall x y. c x y -> p x y) -> Catalyst r c a b -> p a b
runFree interp = \case
  LiftC c -> interp c
  Fst -> fst'
  Snd -> snd'
  Copy -> copy
  Consume -> consume
  Swap -> swap
  SwapE -> swapE
  Reassoc -> reassoc
  ReassocE -> reassocE
  InjectL -> injectL
  InjectR -> injectR
  Unify -> unify
  Tag -> tag
  First p -> first' (runFree interp p)
  Second p -> second' (runFree interp p)
  Alongside l r -> runFree interp l C.*** runFree interp r
  Fanout l r -> runFree interp l C.&&& runFree interp r
  Left' p -> left (runFree interp p)
  Right' p -> right (runFree interp p)
  Fanin l r -> runFree interp l C.||| runFree interp r
  EitherOf l r -> runFree interp l C.+++ runFree interp r
  Comp l r -> runFree interp l C.>>> runFree interp r
  ID -> C.id
  RecurseL l -> recurseL (runFree interp l)
  RecurseR r -> recurseR (runFree interp r)
  FixL l -> fixL (runFree interp l)
  FixR r -> fixR (runFree interp r)

liftC :: c a b -> Catalyst r c a b
liftC = LiftC

instance (r ~ 'Req 'HasCategory symP symS monP monS cart cocart rec fix)
         => C.Category (Catalyst r c) where
  id = ID
  (.) = flip Comp

instance (r ~ 'Req 'HasCategory 'HasSymmetricProduct symS 'HasMonoidalProduct monS 'HasCartesian cocart rec fix)
  => C.Cartesian (Catalyst r c) where
  copy = Copy
  consume = Consume
  fst' = Fst
  snd' = Snd

instance (r ~ 'Req 'HasCategory symP 'HasSymmetricSum monP 'HasMonoidalSum cart 'HasCocartesian rec fix)
         => C.Cocartesian (Catalyst r c) where
  injectL = InjectL
  injectR = InjectR
  unify = Unify
  tag = Tag

instance (r ~ 'Req 'HasCategory 'HasSymmetricProduct symS monP monS cart cocart rec fix) => C.SymmetricProduct (Catalyst r c) where
  swap = Swap
  reassoc = Reassoc

instance (r ~ 'Req 'HasCategory symP 'HasSymmetricSum monP monS cart cocart rec fix) => C.SymmetricSum (Catalyst r c) where
  swapE = SwapE
  reassocE = ReassocE

instance (r ~ 'Req 'HasCategory 'HasSymmetricProduct symS 'HasMonoidalProduct monS cart cocart rec fix) => C.MonoidalProduct (Catalyst r c) where
  (***) = Alongside
  first' = First
  second' = Second

instance (r ~ 'Req 'HasCategory symP 'HasSymmetricSum monP 'HasMonoidalSum cart cocart rec fix) => C.MonoidalSum (Catalyst r c) where
  (+++) = EitherOf
  left = Left'
  right = Right'

instance (r ~ 'Req 'HasCategory symP symS monP monS cart cocart 'HasRecursive fix) => Recursive (Catalyst r c) where
  recurseL = RecurseL
  recurseR = RecurseR

instance (r ~ 'Req 'HasCategory symP symS monP monS cart cocart rec 'HasFixed) => Fixed (Catalyst r c) where
  fixL = FixL
  fixR = FixR