proarrow
Safe HaskellNone
LanguageGHC2024

Proarrow.Tools.SMC

Description

A small HOAS front end for building morphisms in any symmetric monoidal category, the linear counterpart of Proarrow.Tools.CCC. Each variable is used exactly once: the functions on terms are linear, so GHC's linear types check that every variable is used once, and a Term is indexed by its context, which is exactly the variables it uses. So a variable is the identity on its own type, and no copying or discarding is ever generated. Terms with disjoint contexts combine by merging the contexts, which only reorders wires. Functions on terms need LinearTypes and linear arrows, e.g. Term d g a %1 -> Term d g b.

Types are SYN expressions, interpreted in the target category by Interp. Their tensor is a constructor, so split can take a term's type apart, which the target's own **, a type family, would not allow.

Every variable has an id, the number of binders around it, and a context lists its variables by descending id. Merging compares ids, so it only reduces where the depths are known, which is the case for terms built directly inside toSMC. A reusable piece that binds variables of its own is compiled on its own and used through call.

The module also provides do notation, for use with QualifiedDo:

import Prelude hiding ((*))
import Proarrow.Tools.SMC (SYN (..), lift, toSMC, (*))
import Proarrow.Tools.SMC qualified as SMC

rot2T = toSMC @(F a :** F b :** F c) \x -> SMC.do
  (b, c, a) <- lift @(F a :** F b :** F c) @(F b :** F c :** F a) rotT x
  c * a * b

Without the import of (*), Prelude's is used on terms, and GHC reports that as variables used more than once (Many against One) and mismatched depths, not as a missing instance.

A bind takes its right hand side apart with a pattern of (nested) tuples, as split does, and the rest of the block must use every variable exactly once. The argument of toSMC is such a pattern too, as in rotT, and () there compiles a term without inputs.

With RecursiveDo, a rec block traces, so it needs a traced monoidal category. Its variables that are used before they are bound are fed back, and the others are passed on to the rest of the block. GHC's translation of rec passes every variable of the block to its end again, including the ones a later statement of the block already used, so only such blocks work where no statement uses a variable bound by an earlier one. A block of a single statement always qualifies, and a nested pattern lets one statement bind everything:

rec ((am, bp), (bm, cp)) <- lift g (ap * bm) * lift f (bp * cm)

loop traces without GHC's translation, so it has neither restriction, but the type of the fed back variable has to be given.

The module is inspired by Bernardy and Spiwack, Evaluating Linear Functions to Symmetric Monoidal Categories, whose P k r a ports correspond to Term, encode to lift, decode to toSMC, (!:) to (*) and split to split. Unlike their implementation, it keeps the context thinned instead of computing in the cartesian structure and arguing afterwards that the result is monoidal.

Synopsis

Types

data SYN k Source Github #

Type expressions over the objects of k: an object of k, the unit, the tensor, the internal hom and the dual, and the additives: the product and its unit Top, and the coproduct and its unit Zero.

Constructors

F k 
I 
(SYN k) :** (SYN k) infixl 7 
(SYN k) :-> (SYN k) infixr 5 
D (SYN k) 
(SYN k) :&& (SYN k) infixl 6 
Top 
(SYN k) :|| (SYN k) infixl 6 
Zero 

Instances

Instances details
KnownCtx ('[] :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

ctxCase :: (('[] :: [(Nat, SYN k)]) ~ ('[] :: [(Nat, SYN k)]) => r) -> (forall (n :: Nat) (a :: SYN k) (g' :: [(Nat, SYN k)]). (('[] :: [(Nat, SYN k)]) ~ ('(n, a) ': g'), KnownObj a, KnownCtx g') => r) -> r

(Monoidal k, KnownCtx g2) => Merge ('[] :: [(Nat, SYN k)]) (g2 :: Ctx k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('[] :: [(Nat, SYN k)]) g2)) ~> (Interp (Mul ('[] :: [(Nat, SYN k)])) ** Interp (Mul g2)) Source Github #

unmerge :: (Interp (Mul ('[] :: [(Nat, SYN k)])) ** Interp (Mul g2)) ~> Interp (Mul (Union ('[] :: [(Nat, SYN k)]) g2)) Source Github #

(KnownObj a, KnownCtx g) => KnownCtx ('(n, a) ': g :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

ctxCase :: (('(n, a) ': g) ~ ('[] :: [(Nat, SYN k)]) => r) -> (forall (n0 :: Nat) (a0 :: SYN k) (g' :: [(Nat, SYN k)]). (('(n, a) ': g) ~ ('(n0, a0) ': g'), KnownObj a0, KnownCtx g') => r) -> r

(Monoidal k, KnownCtx ('(n, a) ': g1)) => Merge ('(n, a) ': g1 :: [(Nat, SYN k)]) ('[] :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('(n, a) ': g1) ('[] :: [(Nat, SYN k)]))) ~> (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('[] :: [(Nat, SYN k)]))) Source Github #

unmerge :: (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('[] :: [(Nat, SYN k)]))) ~> Interp (Mul (Union ('(n, a) ': g1) ('[] :: [(Nat, SYN k)]))) Source Github #

(Monoidal k, KnownObj a, KnownObj b, KnownCtx g1, KnownCtx g2, MergeBy (CmpNat n m) ('(n, a) ': g1) ('(m, b) ': g2)) => Merge ('(n, a) ': g1 :: [(Natural, SYN k)]) ('(m, b) ': g2 :: [(Natural, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('(n, a) ': g1) ('(m, b) ': g2))) ~> (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('(m, b) ': g2))) Source Github #

unmerge :: (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('(m, b) ': g2))) ~> Interp (Mul (Union ('(n, a) ': g1) ('(m, b) ': g2))) Source Github #

(Monoidal k, KnownObj a) => RecVars k (Term d '['(n, a)] a) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: Term d '['(n, a)] a

consume :: Term d '['(n, a)] a %1 -> r %1 -> r

type family Interp (s :: SYN k) :: k where ... Source Github #

The object of k a type expression stands for.

Equations

Interp ('F a :: SYN k) = a 
Interp ('I :: SYN k) = Unit :: k 
Interp (a ':** b :: SYN k) = Interp a ** Interp b 
Interp (a ':-> b :: SYN k) = Interp a ~~> Interp b 
Interp ('D a :: SYN k) = Dual (Interp a) 
Interp (a ':&& b :: SYN k) = Interp a && Interp b 
Interp ('Top :: SYN k) = TerminalObject :: k 
Interp (a ':|| b :: SYN k) = Interp a || Interp b 
Interp ('Zero :: SYN k) = InitialObject :: k 

class CategoryOf k => KnownObj (s :: SYN k) where Source Github #

Type expressions whose Interp is an object, given that their leaves are.

Methods

withSynOb :: (Ob (Interp s) => r) -> r Source Github #

Instances

Instances details
Monoidal k => KnownObj ('I :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp ('I :: SYN k)) => r) -> r Source Github #

HasTerminalObject k => KnownObj ('Top :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp ('Top :: SYN k)) => r) -> r Source Github #

HasInitialObject k => KnownObj ('Zero :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp ('Zero :: SYN k)) => r) -> r Source Github #

(StarAutonomous k, KnownObj a) => KnownObj ('D a :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp ('D a)) => r) -> r Source Github #

(CategoryOf k, Ob a) => KnownObj ('F a :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp ('F a)) => r) -> r Source Github #

(HasBinaryProducts k, KnownObj a, KnownObj b) => KnownObj (a ':&& b :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp (a ':&& b)) => r) -> r Source Github #

(Monoidal k, KnownObj a, KnownObj b) => KnownObj (a ':** b :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp (a ':** b)) => r) -> r Source Github #

(Closed k, KnownObj a, KnownObj b) => KnownObj (a ':-> b :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp (a ':-> b)) => r) -> r Source Github #

(HasBinaryCoproducts k, KnownObj a, KnownObj b) => KnownObj (a ':|| b :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

withSynOb :: (Ob (Interp (a ':|| b)) => r) -> r Source Github #

synOb :: forall {k} (s :: SYN k). KnownObj s => Obj (Interp s) Source Github #

The identity on the object a type expression stands for.

Terms

data Term (d :: Nat) (g :: Ctx k) (a :: SYN k) where Source Github #

A term at binding depth d with context g and type a: a morphism from the tensor of the context to a.

Constructors

MkTerm :: forall {k} (g :: Ctx k) (a :: SYN k) (d :: Nat). (Interp (Mul g) ~> Interp a) -> Term d g a 

Instances

Instances details
(Monoidal k, KnownObj a) => RecVars k (Term d '['(n, a)] a) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: Term d '['(n, a)] a

consume :: Term d '['(n, a)] a %1 -> r %1 -> r

(cont ~ Term (d + PSize t) (CtxOf cont :: Ctx k) (TyOf cont :: SYN k), r ~ Term d (PCtx t d g a (CtxOf cont :: Ctx k)) (TyOf cont :: SYN k), Pat k t d g a (CtxOf cont :: Ctx k) (TyOf cont :: SYN k)) => Bind k (Term d g a) t 'One cont r Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Term d g a %1 -> (t %1 -> cont) %1 -> r Source Github #

(Bind k (Term d g a) t 'One cont r', r ~ Ret tt r') => Bind k (Term d g a) t 'One (Ret tt cont) r Source Github #

The statement of a rec block, whose continuation is its return.

Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Term d g a %1 -> (t %1 -> Ret tt cont) %1 -> r Source Github #

toSMC :: forall {k} (a :: SYN k) (b :: SYN k) t cont. (Monoidal k, Binds t 0 a cont ('[] :: [(Nat, SYN k)]) b) => (t %1 -> cont) -> Interp a ~> Interp b Source Github #

Compile a function on terms to a morphism. Its argument is a pattern, as on the left of a bind in do notation: a variable, which must be used exactly once, () for the unit, or a pair of patterns.

lift :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k). CategoryOf k => (Interp a ~> Interp b) -> Term d g a %1 -> Term d g b Source Github #

Lift a morphism of the target category to a function on terms.

dup :: forall {k} (s :: SYN k) (d :: Nat) (g :: Ctx k). Comonoid (Interp s) => Term d g s %1 -> Term d g (s ':** s) Source Github #

Copy a term whose type is a comonoid, in Proarrow.Tools.SMC: (x1, x2) <- dup x.

drop :: forall {k} (s :: SYN k) (d :: Nat) (g :: Ctx k). Comonoid (Interp s) => Term d g s %1 -> Term d g ('I :: SYN k) Source Github #

Discard a term whose type is a comonoid, in Proarrow.Tools.SMC: () <- drop x.

call :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k) t cont. (Monoidal k, Binds t 0 a cont ('[] :: [(Nat, SYN k)]) b) => (t %1 -> cont) -> Term d g a %1 -> Term d g b Source Github #

Use a function on terms inside another term, compiled on its own with toSMC, so that its argument is a pattern too. This is how a reusable piece that binds variables of its own is used, and unlike lift of the compiled morphism it needs no type annotations.

(*) :: forall {k} (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k) (a :: SYN k) (b :: SYN k). (Monoidal k, Merge g1 g2) => Term d g1 a %1 -> Term d g2 b %1 -> Term d (Union g1 g2) (a ':** b) infixl 7 Source Github #

Two terms side by side. Their contexts must be disjoint.

split :: forall {k} (d :: Nat) (g :: Ctx k) (r :: Ctx k) (a :: SYN k) (b :: SYN k) (c :: SYN k) (da :: Nat) (db :: Nat). (Monoidal k, KnownObj a, KnownObj b, Merge r g) => Term d g (a ':** b) %1 -> (Term da '['(d, a)] a %1 -> Term db '['(d + 1, b)] b %1 -> Term (d + 2) ('(d + 1, b) ': ('(d, a) ': r)) c) %1 -> Term d (Union r g) c Source Github #

Take a tensor apart: the continuation gets a variable for each side and must use both.

unit :: forall {k} (d :: Nat). Monoidal k => Term d ('[] :: [(Nat, SYN k)]) ('I :: SYN k) Source Github #

The unit, which uses no variables.

lam :: forall {k} (d :: Nat) (r :: Ctx k) (a :: SYN k) (b :: SYN k) t cont. (Closed k, Binds t d a cont r b) => (t %1 -> cont) %1 -> Term d r (a ':-> b) Source Github #

Bind a pattern, as for toSMC, whose variables the body must use exactly once. This needs the category to be closed.

loop :: forall {k} (u :: SYN k) (b :: SYN k) (d :: Nat) (r :: Ctx k) t cont. (TracedMonoidal k, KnownObj b, Binds t d u cont r (b ':** u)) => (t %1 -> cont) %1 -> Term d r b Source Github #

Trace: bind a pattern, as for toSMC, for the value fed back, whose variables the body must use exactly once, and return it again next to the result. This needs the category to be traced.

produce :: forall {k} (a :: SYN k) (d :: Nat). (CompactClosed k, KnownObj a) => Term d ('[] :: [(Nat, SYN k)]) (a ':** 'D a) Source Github #

A new pair of wires, a variable and its dual, from nothing: the unit of the duality. This needs the category to be compact closed.

annihilate :: forall {k} (a :: SYN k) (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k). (IsoMix k, KnownObj a, Merge g1 g2) => Consumer d g1 a %1 -> Term d g2 a %1 -> Term d (Union g1 g2) ('I :: SYN k) Source Github #

Join a dual and its wire into nothing: the counit of the duality, which an isomix category has.

(!) :: forall {k} (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k) (a :: SYN k) (b :: SYN k). (Closed k, KnownObj a, KnownObj b, Merge g1 g2) => Term d g1 (a ':-> b) %1 -> Term d g2 a %1 -> Term d (Union g1 g2) b infixl 8 Source Github #

Function application. The function and its argument must have disjoint contexts.

Inputs and outputs

In a *-autonomous category a term of D a consumes an a. Terms then read as in System L (the μμ̃-calculus): a Term produces, a Consumer consumes, and a Command is the two meeting in a cut, a term of D I. A command can have any number of inputs and outputs. None of this needs compact closure: produce does.

type Consumer (d :: Nat) (g :: Ctx k) (a :: SYN k) = Term d g ('D a) Source Github #

A consumer of a: a term of its dual.

type Command (d :: Nat) (g :: Ctx k) = Term d g ('D ('I :: SYN k)) Source Github #

A producer and a consumer meeting: a term of the unit of par.

type (:##) (a :: SYN k) (b :: SYN k) = 'D ('D a ':** 'D b) infixl 7 Source Github #

Par, the dual of the tensor of the duals, interpreted as Par.

cut :: forall {k} (a :: SYN k) (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k). (StarAutonomous k, KnownObj a, Merge g1 g2) => Consumer d g1 a %1 -> Term d g2 a %1 -> Command d (Union g1 g2) Source Github #

A consumer meets a producer: cut k t gives t to k, like applying a continuation. This needs the category to be *-autonomous.

(|>) :: forall {k} (a :: SYN k) (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k). (StarAutonomous k, KnownObj a, Merge g2 g1) => Term d g1 a %1 -> Consumer d g2 a %1 -> Command d (Union g2 g1) infixl 1 Source Github #

cut with the producer first, as System L writes ⟨t | k⟩: t |> k sends t into k.

accept :: forall {k} (d :: Nat) (r :: Ctx k) (a :: SYN k) t cont. (StarAutonomous k, Binds t d a cont r ('D ('I :: SYN k))) => (t %1 -> cont) %1 -> Consumer d r a Source Github #

Bind an input: a pattern for an a, as for toSMC, whose variables the command must use exactly once, gives a consumer of a. As logic, this refutes a.

emit :: forall {k} (d :: Nat) (r :: Ctx k) (a :: SYN k) (da :: Nat). (StarAutonomous k, KnownObj a, KnownCtx r) => (Consumer da '['(d, 'D a)] a %1 -> Command (d + 1) ('(d, 'D a) ': r)) %1 -> Term d r a Source Github #

Bind an output: a consumer of a, which the command must use exactly once, gives a producer of a. As logic, this is proof by contradiction, and in Haskell it is callCC: doubleNeg after accept.

par :: forall {k} (d :: Nat) (r :: Ctx k) (a :: SYN k) (b :: SYN k) (da :: Nat) (db :: Nat). (StarAutonomous k, KnownObj a, KnownObj b, KnownCtx r) => (Consumer da '['(d, 'D a)] a %1 -> Consumer db '['(d + 1, 'D b)] b %1 -> Command (d + 2) ('(d + 1, 'D b) ': ('(d, 'D a) ': r))) %1 -> Term d r (a :## b) Source Github #

Bind two outputs: consumers of a and of b, which the command must use exactly once each, give a producer of their par.

both :: forall {k} (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k) (a :: SYN k) (b :: SYN k). (StarAutonomous k, KnownObj a, KnownObj b, Merge g1 g2) => Consumer d g1 a %1 -> Consumer d g2 b %1 -> Consumer d (Union g1 g2) (a :## b) Source Github #

A consumer of a par from a consumer of each side. Cutting the par against the tensor of the two consumers does the same with less structure.

Additives

The additives share their context between alternatives, of which only one is used. Terms that share variables can't both be written in a linear function, so the alternatives are functions of their own, compiled with toSMC like the argument of call, and what they share is passed in as one term.

with :: forall {k} (s :: SYN k) (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k) t1 cont1 t2 cont2. (Monoidal k, HasBinaryProducts k, Binds t1 0 s cont1 ('[] :: [(Nat, SYN k)]) a, Binds t2 0 s cont2 ('[] :: [(Nat, SYN k)]) b) => (t1 %1 -> cont1) -> (t2 %1 -> cont2) -> Term d g s %1 -> Term d g (a ':&& b) Source Github #

Both of two alternatives on the same input: the product. Each alternative takes the input with a pattern, as for toSMC. This needs products.

exl :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k). (HasBinaryProducts k, KnownObj a, KnownObj b) => Term d g (a ':&& b) %1 -> Term d g a Source Github #

The first alternative of a product.

exr :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k). (HasBinaryProducts k, KnownObj a, KnownObj b) => Term d g (a ':&& b) %1 -> Term d g b Source Github #

The second alternative of a product.

absorb :: forall {k} (s :: SYN k) (d :: Nat) (g :: Ctx k). (HasTerminalObject k, KnownObj s) => Term d g s %1 -> Term d g ('Top :: SYN k) Source Github #

Use up a term into the unit of the product.

inl :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k). (HasBinaryCoproducts k, KnownObj a, KnownObj b) => Term d g a %1 -> Term d g (a ':|| b) Source Github #

The left injection into a coproduct.

inr :: forall {k} (a :: SYN k) (b :: SYN k) (d :: Nat) (g :: Ctx k). (HasBinaryCoproducts k, KnownObj a, KnownObj b) => Term d g b %1 -> Term d g (a ':|| b) Source Github #

The right injection into a coproduct.

caseOf :: forall {k} (s :: SYN k) (a :: SYN k) (b :: SYN k) (c :: SYN k) (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k) t1 cont1 t2 cont2. (Distributive k, KnownObj s, KnownObj a, KnownObj b, Merge g1 g2, Binds t1 0 (s ':** a) cont1 ('[] :: [(Nat, SYN k)]) c, Binds t2 0 (s ':** b) cont2 ('[] :: [(Nat, SYN k)]) c) => Term d g1 s %1 -> Term d g2 (a ':|| b) %1 -> (t1 %1 -> cont1) -> (t2 %1 -> cont2) -> Term d (Union g1 g2) c Source Github #

Case analysis on a coproduct, given first a term to share between the branches. Both branches take the pair of the shared term and the contents of their alternative with a pattern, as for toSMC. This needs the tensor to distribute over the coproduct.

absurd :: forall {k} (s :: SYN k) (c :: SYN k) (d :: Nat) (g1 :: Ctx k) (g2 :: Ctx k). (Distributive k, KnownObj s, KnownObj c, Merge g1 g2) => Term d g1 s %1 -> Term d g2 ('Zero :: SYN k) %1 -> Term d (Union g1 g2) c Source Github #

There is no term of Zero, so from one, together with the rest of the context, anything follows.

Contexts

type Ctx k = [(Nat, SYN k)] Source Github #

A context: the variables a term uses, each with its id and type, by descending id.

type family Mul (g :: Ctx k) :: SYN k where ... Source Github #

The type standing in for a context: the tensor of its variables' types, with the most recently bound variable on the right. A single variable is just its type, so a variable is the identity. The cost is that Mul ('(n, a) ': g) only reduces once g is known to be empty or not, which ctxCase tells.

Equations

Mul ('[] :: [(Nat, SYN k)]) = 'I :: SYN k 
Mul ('['(n, a)] :: [(Nat, SYN k)]) = a 
Mul ('(n, a) ': g :: [(Nat, SYN k)]) = Mul g ':** a 

class KnownCtx (g :: Ctx k) Source Github #

A context that is known to be empty or not, all the way down.

Minimal complete definition

ctxCase

Instances

Instances details
KnownCtx ('[] :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

ctxCase :: (('[] :: [(Nat, SYN k)]) ~ ('[] :: [(Nat, SYN k)]) => r) -> (forall (n :: Nat) (a :: SYN k) (g' :: [(Nat, SYN k)]). (('[] :: [(Nat, SYN k)]) ~ ('(n, a) ': g'), KnownObj a, KnownCtx g') => r) -> r

(KnownObj a, KnownCtx g) => KnownCtx ('(n, a) ': g :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

ctxCase :: (('(n, a) ': g) ~ ('[] :: [(Nat, SYN k)]) => r) -> (forall (n0 :: Nat) (a0 :: SYN k) (g' :: [(Nat, SYN k)]). (('(n, a) ': g) ~ ('(n0, a0) ': g'), KnownObj a0, KnownCtx g') => r) -> r

ctxOb :: forall {k} (g :: Ctx k). (Monoidal k, KnownCtx g) => Obj (Interp (Mul g)) Source Github #

The identity on the tensor of a context.

withCtxOb :: forall {k} (g :: Ctx k) r. (Monoidal k, KnownCtx g) => (Ob (Interp (Mul g)) => r) -> r Source Github #

The tensor of a context is an object.

type family Union (g1 :: Ctx k) (g2 :: Ctx k) :: Ctx k where ... Source Github #

The context of two terms used side by side.

Equations

Union ('[] :: [(Nat, SYN k)]) (g2 :: Ctx k) = g2 
Union (g1 :: Ctx k) ('[] :: [(Nat, SYN k)]) = g1 
Union ('(n, a) ': g1 :: [(Nat, SYN k)]) ('(m, b) ': g2 :: [(Nat, SYN k)]) = UnionBy (CmpNat n m) ('(n, a) ': g1) ('(m, b) ': g2) 

class (KnownCtx g1, KnownCtx g2) => Merge (g1 :: Ctx k) (g2 :: Ctx k) where Source Github #

Split the tensor of a merged context into the tensors of the two contexts it came from, and back. This is where the wires are reordered, and the only place swap is used.

Methods

merge :: Interp (Mul (Union g1 g2)) ~> (Interp (Mul g1) ** Interp (Mul g2)) Source Github #

unmerge :: (Interp (Mul g1) ** Interp (Mul g2)) ~> Interp (Mul (Union g1 g2)) Source Github #

Instances

Instances details
(Monoidal k, KnownCtx g2) => Merge ('[] :: [(Nat, SYN k)]) (g2 :: Ctx k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('[] :: [(Nat, SYN k)]) g2)) ~> (Interp (Mul ('[] :: [(Nat, SYN k)])) ** Interp (Mul g2)) Source Github #

unmerge :: (Interp (Mul ('[] :: [(Nat, SYN k)])) ** Interp (Mul g2)) ~> Interp (Mul (Union ('[] :: [(Nat, SYN k)]) g2)) Source Github #

(Monoidal k, KnownCtx ('(n, a) ': g1)) => Merge ('(n, a) ': g1 :: [(Nat, SYN k)]) ('[] :: [(Nat, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('(n, a) ': g1) ('[] :: [(Nat, SYN k)]))) ~> (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('[] :: [(Nat, SYN k)]))) Source Github #

unmerge :: (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('[] :: [(Nat, SYN k)]))) ~> Interp (Mul (Union ('(n, a) ': g1) ('[] :: [(Nat, SYN k)]))) Source Github #

(Monoidal k, KnownObj a, KnownObj b, KnownCtx g1, KnownCtx g2, MergeBy (CmpNat n m) ('(n, a) ': g1) ('(m, b) ': g2)) => Merge ('(n, a) ': g1 :: [(Natural, SYN k)]) ('(m, b) ': g2 :: [(Natural, SYN k)]) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

merge :: Interp (Mul (Union ('(n, a) ': g1) ('(m, b) ': g2))) ~> (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('(m, b) ': g2))) Source Github #

unmerge :: (Interp (Mul ('(n, a) ': g1)) ** Interp (Mul ('(m, b) ': g2))) ~> Interp (Mul (Union ('(n, a) ': g1) ('(m, b) ': g2))) Source Github #

snoc :: forall {k} (n :: Nat) (a :: SYN k) (g :: Ctx k). (Monoidal k, KnownObj a, KnownCtx g) => (Interp (Mul g) ** Interp a) ~> Interp (Mul ('(n, a) ': g)) Source Github #

A new variable on the right of a context: a unitor if the context was empty, and nothing otherwise.

snoc2 :: forall {k} (n :: Nat) (a :: SYN k) (m :: Nat) (b :: SYN k) (g :: Ctx k). (Monoidal k, KnownObj a, KnownObj b, KnownCtx g) => (Interp (Mul g) ** (Interp a ** Interp b)) ~> Interp (Mul ('(m, b) ': ('(n, a) ': g))) Source Github #

Two new variables on the right of a context, from their tensor: a unitor if the context was empty, and an associator otherwise.

push2 :: forall {k} (r :: Ctx k) (n :: Nat) (a :: SYN k) (m :: Nat) (b :: SYN k) (g :: Ctx k). (Monoidal k, KnownObj a, KnownObj b, Merge r g) => (Interp (Mul g) ~> (Interp a ** Interp b)) -> Interp (Mul (Union r g)) ~> Interp (Mul ('(m, b) ': ('(n, a) ': r))) Source Github #

Two new variables (n, a) and (m, b) on the right of the context r, for split. When r is empty the pair is the whole context.

Do notation

(>>=) :: Bind k m t p cont r => m %1 -> (t %p -> cont) %1 -> r Source Github #

Bind the right hand side to the pattern of the continuation.

return :: forall k t (d :: Nat). (Monoidal k, RecVars k t, KnownCtx (Vars k t)) => t %1 -> Ret t (Term d (Vars k t) (Mul (Vars k t))) Source Github #

The end of a rec block: all its variables, as the tensor of their context.

mfix :: forall {k} t (d :: Nat) (g :: Ctx k). (TracedMonoidal k, RecVars k t, Merge (Inter g (Vars k t)) (Minus g (Vars k t)), Merge (Inter g (Vars k t)) (Minus (Vars k t) g), Union (Inter g (Vars k t)) (Minus g (Vars k t)) ~ g, Union (Inter g (Vars k t)) (Minus (Vars k t) g) ~ Vars k t) => (t -> Ret t (Term d g (Mul (Vars k t)))) %1 -> Rec d t (Minus g (Vars k t)) (Minus (Vars k t) g) Source Github #

Trace a rec block: the variables it uses before binding them are fed back.

fail :: a Source Github #

GHC's translation of rec refers to fail, but pairs of variables always match.

class Bind k m t (p :: Multiplicity) cont r | m -> k p Source Github #

A bind in a do block: a term taken apart by a pattern, or the variables of a rec block. The multiplicity p of the continuation depends only on the right hand side m, since GHC needs it before it knows the rest.

Minimal complete definition

(>>=)

Instances

Instances details
(cont ~ Term (d + PSize t) (CtxOf cont :: Ctx k) (TyOf cont :: SYN k), r ~ Term d (PCtx t d g a (CtxOf cont :: Ctx k)) (TyOf cont :: SYN k), Pat k t d g a (CtxOf cont :: Ctx k) (TyOf cont :: SYN k)) => Bind k (Term d g a) t 'One cont r Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Term d g a %1 -> (t %1 -> cont) %1 -> r Source Github #

(Bind k (Term d g a) t 'One cont r', r ~ Ret tt r') => Bind k (Term d g a) t 'One (Ret tt cont) r Source Github #

The statement of a rec block, whose continuation is its return.

Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Term d g a %1 -> (t %1 -> Ret tt cont) %1 -> r Source Github #

(SymMonoidal k, RecVars k t, t' ~ t, cont ~ Term (HeadId (Vars k t) + 1) (CtxOf cont :: Ctx k) (TyOf cont :: SYN k), r ~ Term d (Union (Minus (CtxOf cont :: Ctx k) outs) g0) (TyOf cont :: SYN k), AllIn outs (CtxOf cont :: Ctx k), NoneIn (Minus (CtxOf cont :: Ctx k) outs) (Vars k t), Merge (Minus (CtxOf cont :: Ctx k) outs) g0, Merge (Minus (CtxOf cont :: Ctx k) outs) outs, (CtxOf cont :: Ctx k) ~ Union (Minus (CtxOf cont :: Ctx k) outs) outs) => Bind k (Rec d t g0 outs) t' 'Many cont r Source Github #

The rest of the do block after a rec block. GHC binds the variables of the block here without linearity, so the context checks see to it that the ones passed on are used once and the fed back ones not at all.

Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Rec d t g0 outs %1 -> (t' -> cont) %1 -> r Source Github #

type Binds t (n :: Nat) (a :: SYN k) cont (g :: Ctx k) (b :: SYN k) = (KnownObj a, KnownCtx g, Bind k (Term (n + 1) '['(n, a)] a) t 'One cont (Term (n + 1) ('(n, a) ': g) b)) Source Github #

The pattern t of a binder: it takes apart the variable (n, a), and the binder's body cont then gives a term at depth n + 1 with type b, whose context is that variable and g. Both the variable's type and the rest of the context must be known.

class Pat k t (d :: Nat) (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github #

A pattern: a variable, (), or a pair of patterns. A triple or quadruple stands for pairs nested to the left, as a :** b :** c is: (x, y, z) is ((x, y), z). Binding it at depth d to a term with context g and type a, with a continuation with context g' and type c.

Minimal complete definition

pat

Instances

Instances details
(Monoidal k, a ~ ('I :: SYN k), KnownObj c, Merge g g') => Pat k () d (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github #

The pattern () uses up a term of the unit type.

Instance details

Defined in Proarrow.Tools.SMC

Methods

pat :: Term d g a %1 -> (() %1 -> Term (d + PSize ()) g' c) %1 -> Term d (PCtx () d g a g') c

t ~ Term (DepthOf t) g a => Pat k t d (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

pat :: Term d g a %1 -> (t %1 -> Term (d + PSize t) g' c) %1 -> Term d (PCtx t d g a g') c

(Monoidal k, a ~ (a1 ':** a2), KnownObj a1, KnownObj a2, Pat k x (d + 2) '['(d, a1)] a1 (PCtx y ((d + 2) + PSize x) '['(d + 1, a2)] a2 g') c, Pat k y ((d + 2) + PSize x) '['(d + 1, a2)] a2 g' c, (((d + 2) + PSize x) + PSize y) ~ (d + PSize (x, y)), PCtxPair x y d a1 a2 g' ~ ('(d + 1, a2) ': ('(d, a1) ': Drop2 (PCtxPair x y d a1 a2 g'))), Merge (Drop2 (PCtxPair x y d a1 a2 g')) g) => Pat k (x, y) d (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

pat :: Term d g a %1 -> ((x, y) %1 -> Term (d + PSize (x, y)) g' c) %1 -> Term d (PCtx (x, y) d g a g') c

Pat k ((x, y), z) d g a g' c => Pat k (x, y, z) d (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

pat :: Term d g a %1 -> ((x, y, z) %1 -> Term (d + PSize (x, y, z)) g' c) %1 -> Term d (PCtx (x, y, z) d g a g') c

Pat k (((w, x), y), z) d g a g' c => Pat k (w, x, y, z) d (g :: Ctx k) (a :: SYN k) (g' :: Ctx k) (c :: SYN k) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

pat :: Term d g a %1 -> ((w, x, y, z) %1 -> Term (d + PSize (w, x, y, z)) g' c) %1 -> Term d (PCtx (w, x, y, z) d g a g') c

data Ret t x Source Github #

The body of a rec block, tagged with the tuple of its variables. GHC's translation passes that tuple to both return and mfix, and this tag is what makes them the same.

Instances

Instances details
(Bind k (Term d g a) t 'One cont r', r ~ Ret tt r') => Bind k (Term d g a) t 'One (Ret tt cont) r Source Github #

The statement of a rec block, whose continuation is its return.

Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Term d g a %1 -> (t %1 -> Ret tt cont) %1 -> r Source Github #

data Rec (d :: Nat) t (g0 :: Ctx k) (outs :: Ctx k) Source Github #

A rec block after tracing, from its context without the fed back variables to the variables it passes on.

Instances

Instances details
(SymMonoidal k, RecVars k t, t' ~ t, cont ~ Term (HeadId (Vars k t) + 1) (CtxOf cont :: Ctx k) (TyOf cont :: SYN k), r ~ Term d (Union (Minus (CtxOf cont :: Ctx k) outs) g0) (TyOf cont :: SYN k), AllIn outs (CtxOf cont :: Ctx k), NoneIn (Minus (CtxOf cont :: Ctx k) outs) (Vars k t), Merge (Minus (CtxOf cont :: Ctx k) outs) g0, Merge (Minus (CtxOf cont :: Ctx k) outs) outs, (CtxOf cont :: Ctx k) ~ Union (Minus (CtxOf cont :: Ctx k) outs) outs) => Bind k (Rec d t g0 outs) t' 'Many cont r Source Github #

The rest of the do block after a rec block. GHC binds the variables of the block here without linearity, so the context checks see to it that the ones passed on are used once and the fed back ones not at all.

Instance details

Defined in Proarrow.Tools.SMC

Methods

(>>=) :: Rec d t g0 outs %1 -> (t' -> cont) %1 -> r Source Github #

class RecVars k t | t -> k Source Github #

The variables of a rec block, as GHC tuples them up.

Minimal complete definition

recVars, consume

Instances

Instances details
(RecVars k x, RecVars k y) => RecVars k (x, y) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: (x, y)

consume :: (x, y) %1 -> r %1 -> r

(RecVars k x, RecVars k y, RecVars k z) => RecVars k (x, y, z) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: (x, y, z)

consume :: (x, y, z) %1 -> r %1 -> r

(Monoidal k, KnownObj a) => RecVars k (Term d '['(n, a)] a) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: Term d '['(n, a)] a

consume :: Term d '['(n, a)] a %1 -> r %1 -> r

(RecVars k x, RecVars k y, RecVars k z, RecVars k w) => RecVars k (x, y, z, w) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: (x, y, z, w)

consume :: (x, y, z, w) %1 -> r %1 -> r

(RecVars k x, RecVars k y, RecVars k z, RecVars k w, RecVars k v) => RecVars k (x, y, z, w, v) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: (x, y, z, w, v)

consume :: (x, y, z, w, v) %1 -> r %1 -> r

(RecVars k x, RecVars k y, RecVars k z, RecVars k w, RecVars k v, RecVars k u) => RecVars k (x, y, z, w, v, u) Source Github # 
Instance details

Defined in Proarrow.Tools.SMC

Methods

recVars :: (x, y, z, w, v, u)

consume :: (x, y, z, w, v, u) %1 -> r %1 -> r

Examples

swapT :: forall {k} (a :: k) (b :: k). (SymMonoidal k, Ob a, Ob b) => (a ** b) ~> (b ** a) Source Github #

Swap a tensor.

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

applyT :: forall {k} (a :: k) (b :: k). (Closed k, SymMonoidal k, Ob a, Ob b) => ((a ~~> b) ** a) ~> b Source Github #

Apply a function to an argument, both in a tensor.

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

curryT :: forall {k} (a :: k) (b :: k). (Closed k, SymMonoidal k, Ob a, Ob b) => a ~> (b ~~> (a ** b)) Source Github #

Curry the tensor.

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

rotT :: forall {k} (a :: k) (b :: k) (c :: k). (SymMonoidal k, Ob a, Ob b, Ob c) => ((a ** b) ** c) ~> ((b ** c) ** a) Source Github #

Rotate a triple, with a triple pattern.

>>> import Prelude (Bool (..), Int)
>>> rotT @Int @Bool @Int ((1, True), 2)
((True,2),1)

traceT :: forall {k} (a :: k) (b :: k) (u :: k). (TracedMonoidal k, Ob a, Ob b, Ob u) => ((a ** u) ~> (b ** u)) -> a ~> b Source Github #

Trace out u with a rec block. In Type the trace is a lazy fixed point.

>>> import Prelude (Int, take)
>>> traceT @Int @[Int] @[Int] (\(a, u) -> (take 3 u, a : u)) 1
[1,1,1]

loopT :: forall {k} (a :: k) (b :: k) (u :: k). (TracedMonoidal k, Ob a, Ob b, Ob u) => ((a ** u) ~> (b ** u)) -> a ~> b Source Github #

Trace out u with loop.

>>> import Prelude (Int, take)
>>> loopT @Int @[Int] @[Int] (\(a, u) -> (take 3 u, a : u)) 1
[1,1,1]

loopCC :: forall {k} (a :: k) (b :: k) (u :: k). (CompactClosed k, Ob a, Ob b, Ob u) => ((a ** u) ~> (b ** u)) -> a ~> b Source Github #

A trace from the duality alone, so for any compact closed category: feed u in along one end of a new pair and join its new value with the other end.

snakeT :: forall {k} (a :: k). (CompactClosed k, Ob a) => a ~> a Source Github #

A snake: create a pair, join its dual with the input, and continue with the other end. By the zigzag law it is the identity. The input is older than the pair, so it sits to the left of it, and the join needs a swap.

snakeDualT :: forall {k} (a :: k). (CompactClosed k, Ob a) => Dual a ~> Dual a Source Github #

The snake on the dual: join the input with the first end of a new pair, and continue with the second. Here the wires meet in the order they come, so no swap is needed.

combineDualT :: forall {k} (a :: k) (b :: k). (CompactClosed k, Ob a, Ob b) => (Dual a ** Dual b) ~> Dual (a ** b) Source Github #

The inverse of distribDual: make a pair for a ** b, and annihilate the two halves of its plain end with the given duals.

dniT :: forall {k} (a :: k). (StarAutonomous k, Ob a) => a ~> Dual (Dual a) Source Github #

Double negation introduction: a consumer of a consumer of a hands it the a.

dneT :: forall {k} (a :: k). (StarAutonomous k, Ob a) => Dual (Dual a) ~> a Source Github #

Double negation elimination, the classical direction: emit an a by giving its consumer to the input.

contraT :: forall {k} (a :: k) (b :: k). (StarAutonomous k, Ob a, Ob b) => (a ~> b) -> Dual b ~> Dual a Source Github #

Contraposition: a consumer of b consumes a through f.

parSwapT :: forall {k} (a :: k) (b :: k). (StarAutonomous k, Ob a, Ob b) => Par a b ~> Par b a Source Github #

Par is symmetric: emit both outputs and hand them to the input the other way round. This is parSwap.

weakDistT :: forall {k} (a :: k) (b :: k) (c :: k). (StarAutonomous k, Ob a, Ob b, Ob c) => (a ** Par b c) ~> Par (a ** b) c Source Github #

Linear (weak) distributivity, a ⊗ (b ⅋ c) ⊸ (a ⊗ b) ⅋ c: the b the input emits is paired with a and sent to the first output, and its c goes to the second. This is weakDistL.

distT :: forall {k} (a :: k) (b :: k) (c :: k). (Distributive k, SymMonoidal k, Ob a, Ob b, Ob c) => (a ** (b || c)) ~> ((a ** b) || (a ** c)) Source Github #

The tensor distributes over the coproduct: the shared a goes to whichever branch is taken.

>>> import Prelude (Bool (..), Char, Either (..), Int)
>>> distT @Int @Bool @Char (1, Left True)
Left (1,True)

swapEitherT :: forall {k} (a :: k) (b :: k). (Distributive k, SymMonoidal k, Ob a, Ob b) => (a || b) ~> (b || a) Source Github #

Swap a coproduct, with nothing to share.

>>> import Prelude (Bool (..), Either (..), Int)
>>> swapEitherT @Int @Bool (Left 1)
Right 1

bothWaysT :: forall {k} (a :: k) (b :: k). (SymMonoidal k, HasBinaryProducts k, Ob a, Ob b) => (a ** b) ~> ((a ** b) && (b ** a)) Source Github #

A pair both as it is and swapped: each alternative takes the same pair apart in its own way.

>>> import Prelude (Bool (..), Int)
>>> bothWaysT @Int @Bool (1, True)
((1,True),(True,1))