proarrow
Safe HaskellNone
LanguageGHC2024

Proarrow.Tools.CCC

Description

A small HOAS (higher-order abstract syntax) front end for building morphisms in any BiCCC, compiling through the free category of Proarrow.Category.Instance.Free with the bicartesian closed structures rather than a bespoke one. A Free term tracks its free variables via a context list, the way a well-scoped lambda calculus does; lam binds an ordinary Haskell-level variable that Cast automatically "weakens" across nested lambdas so inner lambdas can still refer to outer ones. toCCC interprets a closed term (no free variables) into an actual morphism of the target category.

Synopsis

Documentation

toCCC :: forall {k} (a :: Syntax k) (b :: Syntax k). (BiCCC k, Ob a, Ob b) => Free ('[] :: [Syntax k]) (a ~~> b) -> Lower (Id :: k -> k -> Type) a ~> Lower (Id :: k -> k -> Type) b Source Github #

Interpret a closed term (no free variables) into an actual morphism of the target category: move the empty context from the terminal object to the monoidal unit, lower the function-valued term (a closed one needs no arguments to uncurry), and fold into k with generators interpreted by unwrapping Id (the free category was built over k's own hom-sets directly).

lam :: forall {k} (a :: Syntax k) (b :: Syntax k) (i :: Ctx k). (KnownCtx i, Ob a, Ob b) => ((forall (x :: Ctx k). Cast x (a ': i) => Free x a) -> Free (a ': i) b) -> Free i (a ~~> b) Source Github #

Bind a variable, HOAS-style: the function argument stands for the newly bound variable, usable (via Cast) in the body of this lam and any lam nested inside it. The body is a morphism out of the context product, but curry wants the tensor, so the Cartesian coercion mediates.

($) :: forall {k} (a :: Syntax k) (b :: Syntax k) (i :: Ctx k). (Ob a, Ob b) => Free i (a ~~> b) -> Free i a -> Free i b infixr 0 Source Github #

Function application.

lift :: forall {k} (a :: k) (b :: k) (i :: Ctx k). (Ob a, Ob b) => (a ~> b) -> Free i (F a :: FREE BiCCCStructs (Id :: k -> k -> Type)) -> Free i (F b :: FREE BiCCCStructs (Id :: k -> k -> Type)) Source Github #

Embed a morphism of the target category as a term between embedded objects.

pattern (:&) :: (Ob a, Ob b) => Free i a -> Free i b -> Free i (a && b) Source Github #

either :: forall {k} (a :: Syntax k) (b :: Syntax k) (c :: Syntax k) (i :: Ctx k). (KnownCtx i, Ob a, Ob b, Ob c) => Free i (a ~~> c) -> Free i (b ~~> c) -> Free i (a || b) -> Free i c Source Github #

lft :: forall {k} (a :: Syntax k) (b :: Syntax k) (i :: Ctx k). (Ob a, Ob b) => Free i a -> Free i (a || b) Source Github #

Inject as the left/right branch of a sum.

rgt :: forall {k} (a :: Syntax k) (b :: Syntax k) (i :: Ctx k). (Ob a, Ob b) => Free i b -> Free i (a || b) Source Github #

data Free (i :: Ctx k) (a :: Syntax k) Source Github #

A term with free variables i (innermost/most-recently-bound first) and result type a: a morphism from the context product to a in the free BiCCC. A newtype (rather than a bare type synonym) so that i is recoverable from a Free term's type: Mul is many-to-one at the type-family level as far as GHC's injectivity checker is concerned (even though it's mathematically injective here), which would otherwise leave i ambiguous wherever it has to be inferred rather than given explicitly (e.g. picking which context a HOAS variable reference in lam denotes).

type Syntax k = FREE BiCCCStructs (Id :: k -> k -> Type) Source Github #

The syntax category over k: the free BiCCC on k's own hom-sets, i.e. Id. (~>) itself is an unsaturated type family application, so it isn't allowed as a type index where it is pattern-matched on below, in Mul.

type Ctx k = [Syntax k] Source Github #

type family Mul (i :: Ctx k) :: Syntax k where ... Source Github #

The context product: Mul i is the single object standing in for "all the bound variables in i", a right fold with the most-recently-bound variable last. This mirrors Proarrow.Category.Monoidal.Strictified's Fold, which puts its head leftmost. Fold itself can't be reused, since curry/fst/snd expect the thing being abstracted over on the right of the product.

Equations

Mul ('[] :: [Syntax k]) = TermF :: Syntax k 
Mul (a ': as :: [Syntax k]) = Mul as *! a 

class Cast (i :: Ctx k) (j :: Ctx k) where Source Github #

Cast i j holds when context i is context j with zero or more extra variables pushed on top, letting a term built for j be used anywhere "deeper" than j.

Methods

cast :: forall (a :: Syntax k). Ob a => Free j a -> Free i a Source Github #

Instances

Instances details
Cast (i :: Ctx k) (i :: Ctx k) Source Github # 
Instance details

Defined in Proarrow.Tools.CCC

Methods

cast :: forall (a :: Syntax k). Ob a => Free i a -> Free i a Source Github #

(Cast i j, KnownCtx i, Ob b, (b ': i) ~ i') => Cast (i' :: [Syntax k]) (j :: Ctx k) Source Github # 
Instance details

Defined in Proarrow.Tools.CCC

Methods

cast :: forall (a :: Syntax k). Ob a => Free j a -> Free i' a Source Github #

class KnownCtx (i :: Ctx k) where Source Github #

Methods

ctxOb :: Obj (Mul i) Source Github #

Instances

Instances details
KnownCtx ('[] :: [Syntax k]) Source Github # 
Instance details

Defined in Proarrow.Tools.CCC

Methods

ctxOb :: Obj (Mul ('[] :: [Syntax k])) Source Github #

(KnownCtx i, Ob b) => KnownCtx (b ': i :: [Syntax k]) Source Github # 
Instance details

Defined in Proarrow.Tools.CCC

Methods

ctxOb :: Obj (Mul (b ': i)) Source Github #

type BiCCCStructs = '[HasTerminalObject, HasInitialObject, HasBinaryProducts, HasBinaryCoproducts, Monoidal, Closed, Cartesian, Distributive] Source Github #

The structures of a bicartesian closed category as a list for FREE: the five BiCCC classes, the Cartesian marker supplying the formal tensor = product coercions (the free category cannot state that type equality itself), and Distributive, for case analysis in the presence of a context.

type F (a :: k) = 'EMB a :: FREE cs p Source Github #

A short alias for embedding a base-category object, so type applications built from it read like the target signature: &&, || and ~~> on the free category are its object formers, so (F a ~~> F b) && F a is the object it looks like.

injectRight :: forall {k} (a :: k) (b :: k). (BiCCC k, Ob a, Ob b) => a ~> (b || a) Source Github #

Inject as the right element of a sum.

>>> import Prelude (Bool (..))
>>> injectRight @Bool @Bool True
Right True

swapProduct :: forall {k} (a :: k) (b :: k). (BiCCC k, Ob a, Ob b) => (a && b) ~> (b && a) Source Github #

Swap a product.

>>> import Prelude (Bool (..))
>>> swapProduct @Bool @Bool (True, False)
(False,True)

applyPair :: forall {k} (a :: k) (b :: k). (BiCCC k, Ob a, Ob b) => ((a ~~> b) && a) ~> b Source Github #

Apply a function to an argument, both bundled in a product.

>>> import Prelude (Bool (..), not)
>>> applyPair @Bool @Bool (not, True)
False

curryPair :: forall {k} (a :: k) (b :: k). (BiCCC k, Ob a, Ob b) => a ~> (b ~~> (a && b)) Source Github #

Curry a pairing function.

>>> import Prelude (Bool (..))
>>> curryPair @Bool @Bool True False
(True,False)

flipCurried3 :: forall {k} (a :: k) (b :: k) (c :: k). (BiCCC k, Ob a, Ob b, Ob c) => (b ~~> (a ~~> (b ~~> c))) ~> (a ~~> (b ~~> c)) Source Github #

Flip the argument order of a 3-argument curried function, applying the last argument twice. This exercises three levels of nested lam and Cast weakening across all of them.

>>> import Prelude (Bool (..))
>>> flipCurried3 @Bool @Bool @Bool (\_ a _ -> a) True False
True

swapSum :: forall {k} (a :: k) (b :: k). (BiCCC k, Ob a, Ob b) => (a || b) ~> (b || a) Source Github #

Swap a sum, via either.

>>> import Prelude (Bool (..), Either (..))
>>> swapSum @Bool @Bool (Left True)
Right True
>>> swapSum @Bool @Bool (Right False)
Left False

caseEither :: forall {k} (a :: k) (b :: k) (c :: k). (BiCCC k, Ob a, Ob b, Ob c) => ((a || b) && ((a ~~> c) && (b ~~> c))) ~> c Source Github #

Eliminate a sum by applying whichever of the two functions matches the branch actually present.

>>> import Prelude (Bool (..), Either (..), not)
>>> caseEither @Bool @Bool @Bool (Left True, (not, id))
False
>>> caseEither @Bool @Bool @Bool (Right True, (not, id))
True