| Safe Haskell | None |
|---|---|
| Language | GHC2024 |
Circuit.Poles
Contents
- Channel poles (bi-polar contract)
- Matched pair
- Counit
- Prefixing an action to an
In - Suffixing an action to an
Out - Build a
Polesfrom primitive actions - Extract primitive actions from a
Poles - Sequential composition
- Parallel composition
- Morphism-level mapping
- Dualising object / unit poles (requires constant morphisms)
- Copycat / multiplicative excluded middle
- Boxes
- Additive connectives
Description
Free channel poles over a base arrow, plus concrete box helpers.
A channel has exactly two poles:
Out— the companion (read / emit pole), covariant in the payload.In— the conjoint (write / commit pole), contravariant in the payload.
Poles is the record that pairs one In with one Out. The poles are
defined purely in terms of the base arrow arr.
open produces a matched pair; close plugs the pair back together by
feeding the Out into the In.
A symmetric pole Poles arr a a with close (conjoint p) (companion p) = id
is the copycat strategy for the multiplicative excluded middle A ⅋ A⊥:
it routes traffic between the two poles without ever deciding which side is
true. For the unit object use open (also exported as copycat).
Relationship to Channel
Poles is the bi-polar / effectful API: it is the right tool for
K IO/STM process plumbing where the channel is a write pole paired
with a read pole. For pure (->) Moore-style channels indexed by a
polynomial, prefer Channel.
There is no deprecation shim yet: the relationship between the bi-polar
and polynomial views is still being settled. This module stays unchanged
until the polynomial Channel gains an equivalent effectful story.
Synopsis
- newtype Out (arr :: k -> k1 -> Type) (a :: k1) = Out {}
- newtype In (arr :: k -> k1 -> Type) (a :: k) = In {}
- data Poles (arr :: k -> k1 -> Type) (a :: k) (b :: k1) = Poles {}
- close :: forall {k} arr (a :: k). In arr a -> Out arr a -> arr a a
- prefixIn :: forall {k} arr (a :: k) (b :: k). Category arr => arr a b -> In arr b -> In arr a
- suffixOut :: forall {k} arr (a :: k) (b :: k). Category arr => Out arr a -> arr a b -> Out arr b
- poles :: forall {k} arr (a :: k) (b :: k) (bot :: k). HasDual bot arr => arr a bot -> arr bot b -> Poles arr a b
- poles0 :: HasDual () arr => arr a () -> arr () b -> Poles arr a b
- polesK :: Monad m => (a -> m ()) -> m b -> Poles (K m) a b
- splay :: forall {k} arr (a :: k) (b :: k) (bot :: k). HasDual bot arr => Poles arr a b -> (arr a bot, arr bot b)
- splay0 :: HasDual () arr => Poles arr a b -> (arr a (), arr () b)
- compose :: forall {k} (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (bot :: k). HasDual bot arr => Poles arr a b -> Poles arr b c -> Poles arr a c
- compose0 :: forall (arr :: Type -> Type -> Type) a b c. HasDual () arr => Poles arr a b -> Poles arr b c -> Poles arr a c
- (>:>) :: forall {k} (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (bot :: k). HasDual bot arr => Poles arr a b -> Poles arr b c -> Poles arr a c
- polesTensor :: forall {k} (t :: k -> k -> k) (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (d :: k) (bot :: k). (Tensor t arr, HasDual bot arr, Unit t ~ bot) => Poles arr a b -> Poles arr c d -> Poles arr (t a c) (t b d)
- iomap :: forall {k} arr (a :: k) (a' :: k) (b :: k) (b' :: k). Category arr => arr a' a -> arr b b' -> Poles arr a b -> Poles arr a' b'
- imap :: forall {k} arr (a :: k) (a' :: k) (b :: k). Category arr => arr a' a -> Poles arr a b -> Poles arr a' b
- omap :: forall {k} arr (a :: k) (b :: k) (b' :: k). Category arr => arr b b' -> Poles arr a b -> Poles arr a b'
- class Category arr => HasDual (bot :: k) (arr :: k -> k -> Type) where
- copycat :: forall {k} (arr :: k -> k -> Type) (bot :: k). HasDual bot arr => Poles arr bot bot
- box :: forall {k} (bot :: k) arr (a :: k) (b :: k). HasDual bot arr => Poles arr a b -> arr a b
- boxAsymmetric :: forall {k} (bot :: k) (t :: k -> k -> k) arr (a :: k) (b :: k). (HasDual bot arr, Tensor t arr) => Poles arr a b -> arr (t a bot) (t bot b)
- pair :: forall (arr :: Type -> Type -> Type) a b c. (HasDual () arr, Tensor (,) arr, Copy arr a) => Poles arr a b -> Poles arr a c -> Poles arr a (b, c)
- data Bias
- race :: forall (arr :: Type -> Type -> Type) a b. (HasDual () arr, Tensor (,) arr, Copy arr a, FunctionLike arr) => (b -> Bool) -> Bias -> Poles arr a b -> Poles arr a b -> Poles arr a b
Channel poles (bi-polar contract)
newtype Out (arr :: k -> k1 -> Type) (a :: k1) Source #
Out is the companion of the identity functor. Covariant in a
(sits in the output position).
newtype In (arr :: k -> k1 -> Type) (a :: k) Source #
In is the conjoint of the identity functor. Contravariant in
a (sits in the input position).
Matched pair
data Poles (arr :: k -> k1 -> Type) (a :: k) (b :: k1) Source #
A matched pair of channel poles: one In and one Out.
This is the bi-polar communication contract. The conjoint (In)
consumes payloads of type a; the companion (Out) produces payloads
of type b. For symmetric channels such as queues a = b.
Together with prefixIn and suffixOut, Poles carries an enriched
profunctor structure over the base category arr: prefixIn is the
left action of arr on In poles, and suffixOut is the right action
of arr on Out poles.
Counit
Prefixing an action to an In
prefixIn :: forall {k} arr (a :: k) (b :: k). Category arr => arr a b -> In arr b -> In arr a Source #
Precompose an arr-morphism with an In pole.
Given f :: arr a b and an In pole at type b, produce an In pole
at type a. Running the resulting pole first executes f and then
commits through the original pole.
This is the left (contravariant) action of the base category on In
poles. Specialised to unit poles it is the canonical way to build
effectful write poles.
>>>let polesU = open :: Poles (->) () ()>>>let inA = prefixIn (const ()) (conjoint polesU) :: In (->) Int>>>commit inA (companion polesU) 42()
Suffixing an action to an Out
suffixOut :: forall {k} arr (a :: k) (b :: k). Category arr => Out arr a -> arr a b -> Out arr b Source #
Postcompose an arr-morphism with an Out pole.
Given an Out pole at type a and g :: arr a b, produce an Out
pole at type b. Running the resulting pole first emits through the
original pole and then executes g on the emitted value.
This is the right (covariant) action of the base category on Out
poles. Specialised to unit poles it is the canonical way to build
effectful read poles.
>>>let polesU = open :: Poles (->) () ()>>>let outA = suffixOut (companion polesU) (const 42) :: Out (->) Int>>>emit outA (conjoint polesU) ()42
Build a Poles from primitive actions
poles :: forall {k} arr (a :: k) (b :: k) (bot :: k). HasDual bot arr => arr a bot -> arr bot b -> Poles arr a b Source #
Build a Poles from a write morphism and a read morphism.
write :: arr a bot consumes the input payload and produces the dualising
object; read :: arr bot b consumes the dualising object and produces the
output payload. The dualising-object poles wire the two halves together.
This is the canonical way to turn a pair of primitive channel actions
into a matched pair of In and Out poles.
Compositional spelling:
poles write receive = Poles (prefixIn write (conjoint open)) (suffixOut (companion open) receive)
poles0 :: HasDual () arr => arr a () -> arr () b -> Poles arr a b Source #
Convenience version of poles when the dualising object is ().
polesK :: Monad m => (a -> m ()) -> m b -> Poles (K m) a b Source #
Specialization of poles for K actions.
write :: a -> m () consumes the input payload; receive :: m b
produces the output payload. The dualising-object handling is hidden
inside the K wrappers.
Extract primitive actions from a Poles
splay :: forall {k} arr (a :: k) (b :: k) (bot :: k). HasDual bot arr => Poles arr a b -> (arr a bot, arr bot b) Source #
Extract the primitive write and read actions from a Poles by
plugging each pole with the dualising-object poles.
For a Poles built with poles, this recovers the original
write :: arr a bot and receive :: arr bot b.
>>>let p = poles0 (\() -> ()) (const (42 :: Int)) :: Poles (->) () Int>>>let (write, receive) = splay0 p>>>(write (), receive ())((),42)
splay0 :: HasDual () arr => Poles arr a b -> (arr a (), arr () b) Source #
Convenience version of splay when the dualising object is ().
Sequential composition
compose :: forall {k} (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (bot :: k). HasDual bot arr => Poles arr a b -> Poles arr b c -> Poles arr a c Source #
Sequential composition of Poles.
Given p1 :: Poles arr a b and p2 :: Poles arr b c, produce an
Poles arr a c by connecting the b pole of p1 to the b pole of
p2. The primitive actions are extracted via splay and reassembled
with poles, so box preserves the composition:
box (compose p1 p2) = box p2 . box p1
Identity exists at the chosen unit type: open :: Poles arr u u is
the identity for composition.
>>>let p1 = poles0 (const ()) (const 1 :: () -> Int) :: Poles (->) () Int>>>let p2 = poles0 (const ()) (const 2 :: () -> Int) :: Poles (->) Int Int>>>box @() (compose0 p1 p2) ()2
open at the dualising object is the identity for composition:
>>>let r = poles @(->) @Bool @Bool @Bool not not>>>box @Bool (compose @_ @_ @_ @_ @Bool (open :: Poles (->) Bool Bool) r) TrueTrue
compose0 :: forall (arr :: Type -> Type -> Type) a b c. HasDual () arr => Poles arr a b -> Poles arr b c -> Poles arr a c Source #
Convenience version of compose when the dualising object is ().
(>:>) :: forall {k} (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (bot :: k). HasDual bot arr => Poles arr a b -> Poles arr b c -> Poles arr a c infixr 1 Source #
Forward-composition operator for Poles. p1 >:> p2 = compose p1 p2.
Parallel composition
polesTensor :: forall {k} (t :: k -> k -> k) (arr :: k -> k -> Type) (a :: k) (b :: k) (c :: k) (d :: k) (bot :: k). (Tensor t arr, HasDual bot arr, Unit t ~ bot) => Poles arr a b -> Poles arr c d -> Poles arr (t a c) (t b d) Source #
Parallel composition of Poles.
Pair two Poles side by side on the tensor t. The primitive
actions are tensored and then collapsed to and from the dualising object
with the tensor unitors. This requires the tensor unit to coincide with
the dualising object bot; in practice this is the cartesian (,) tensor
with bot = ().
>>>let p1 = poles0 (const ()) (const 1 :: () -> Int) :: Poles (->) () Int>>>let p2 = poles0 (const ()) (const 2 :: () -> Int) :: Poles (->) () Int>>>box @() (polesTensor p1 p2) ((), ())(1,2)
Morphism-level mapping
iomap :: forall {k} arr (a :: k) (a' :: k) (b :: k) (b' :: k). Category arr => arr a' a -> arr b b' -> Poles arr a b -> Poles arr a' b' Source #
Precompose the input and postcompose the output of a Poles.
This is the morphism-level profunctor action: f :: arr a' a shapes
what the conjoint sees, and g :: arr b b' shapes what the companion
emits.
>>>let p = poles0 (const ()) (const 42 :: () -> Int) :: Poles (->) () Int>>>let p' = iomap (const ()) ((+1) :: Int -> Int) p :: Poles (->) () Int>>>box @() p' ()43
imap :: forall {k} arr (a :: k) (a' :: k) (b :: k). Category arr => arr a' a -> Poles arr a b -> Poles arr a' b Source #
Precompose the input of a Poles.
omap :: forall {k} arr (a :: k) (b :: k) (b' :: k). Category arr => arr b b' -> Poles arr a b -> Poles arr a b' Source #
Postcompose the output of a Poles.
Dualising object / unit poles (requires constant morphisms)
class Category arr => HasDual (bot :: k) (arr :: k -> k -> Type) where Source #
Arrows that have channel poles for a given dualising object bot.
The dualising object is the target of the polar pairing and the object
through which the two poles of a Poles are plugged together. In the
cartesian case it is the monoidal unit (); for halt-mark / delivery
pairings it can be Bool.
The poles are the identity-on-bot morphism split into its two polar
halves. The companion is constant; the conjoint delegates to the
opposing companion.
These poles require the base arrow to support constant morphisms, so they are captured by this class rather than being definable for all arrows.
Methods
open :: Poles arr bot bot Source #
The dualising object as channel poles.
Yank
>>>let poles = open :: Poles (->) () ()>>>close (conjoint poles) (companion poles) ()()
Plug
>>>let polesA = open :: Poles (->) () ()>>>let polesU = open :: Poles (->) () ()>>>commit (conjoint polesA) (companion polesU) ()()>>>emit (companion polesA) (conjoint polesU) ()()
Instances
| Monad m => HasDual () (K m :: Type -> Type -> Type) Source # | Dualising object Same shape as the |
| HasDual () (->) Source # | Dualising object The companion is the constant function returning |
Defined in Circuit.Poles | |
| Monad m => HasDual Bool (K m :: Type -> Type -> Type) Source # | Dualising object Same shape as the |
| HasDual Bool (->) Source # | Dualising object The companion is the constant function returning |
| (Monad m, Pointed s) => HasDual Void (Body Either s (K m) :: Type -> Type -> Type) Source # | Unit poles for |
| Pointed s => HasDual Void (Body Either s (->) :: Type -> Type -> Type) Source # | Unit poles for The coproduct case needs a distinguished element of the carrier |
| Monad m => HasDual () (Body (,) s (K m) :: Type -> Type -> Type) Source # | Unit poles for Same shape as the |
| HasDual () (Body (,) s (->) :: Type -> Type -> Type) Source # | Unit poles for The companion discards its input and returns |
Copycat / multiplicative excluded middle
copycat :: forall {k} (arr :: k -> k -> Type) (bot :: k). HasDual bot arr => Poles arr bot bot Source #
The copycat strategy at the dualising object bot.
This is the multiplicative excluded middle bot ⅋ bot⊥ for arrows that
have poles at bot: a self-dual channel whose close is the identity on
bot. It routes between the two poles without ever deciding which one
holds.
The additive excluded middle bot ⊕ bot⊥ — a verdict, now — is not
supported; there is no decide :: Either bot bot here, because only the
routing witness is provable.
Boxes
box :: forall {k} (bot :: k) arr (a :: k) (b :: k). HasDual bot arr => Poles arr a b -> arr a b Source #
Close a Poles to a plain base-arrow morphism.
A matched pair of free poles (Poles) is a box with one input wire and
one output wire. This helper embeds that box into a traced monoidal
category by unit-plugging the remaining two slots, giving a plain
arr a b: input on the left, output on the right, with the unit plumbing
hidden.
>>>let p = poles0 (const ()) (const 42) :: Poles (->) () Int>>>box @() p ()42
boxAsymmetric :: forall {k} (bot :: k) (t :: k -> k -> k) arr (a :: k) (b :: k). (HasDual bot arr, Tensor t arr) => Poles arr a b -> arr (t a bot) (t bot b) Source #
Asymmetric box with the dualising object exposed on opposite sides.
Uses tensor at the base arrow level. The input carries the dualising object
on the right and the output carries it on the left; most users will prefer
the dualising-object-normalised box.
>>>let p = poles0 (const ()) (const 42) :: Poles (->) () Int>>>boxAsymmetric @() p ((), ())((),42)
Additive connectives
pair :: forall (arr :: Type -> Type -> Type) a b c. (HasDual () arr, Tensor (,) arr, Copy arr a) => Poles arr a b -> Poles arr a c -> Poles arr a (b, c) Source #
Additive conjunction: both sub-poles receive the same input and their outputs are paired.
This is the & connective / await fragment: every branch sees the
input, and the composite emits all of their results.
>>>let p1 = poles0 (const ()) (const 1 :: () -> Int) :: Poles (->) () Int>>>let p2 = poles0 (const ()) (const 2 :: () -> Int) :: Poles (->) () Int>>>box @() (pair p1 p2) ()(1,2)
Bias for ordered choice in scheduling and additive disjunction.
LeftFirst and RightFirst are used by shared-medium fusion in
Circuit.Shared and by additive disjunction in Circuit.Poles.
Constructors
| LeftFirst | |
| RightFirst |
race :: forall (arr :: Type -> Type -> Type) a b. (HasDual () arr, Tensor (,) arr, Copy arr a, FunctionLike arr) => (b -> Bool) -> Bias -> Poles arr a b -> Poles arr a b -> Poles arr a b Source #
Additive disjunction / race: both sub-poles receive the same input, but only the first output satisfying the predicate is emitted.
The predicate selects "silent" values that should be skipped. The bias
chooses which side to prefer when both are non-silent. The picking logic is
lifted into the base arrow via FunctionLike.
>>>let eL = poles0 (const ()) (const (Just 1)) :: Poles (->) () (Maybe Int)>>>let eR = poles0 (const ()) (const (Just 2)) :: Poles (->) () (Maybe Int)>>>box @() (race isNothing LeftFirst eL eR) ()Just 1>>>box @() (race isNothing RightFirst eL eR) ()Just 2