A well-typed symmetric-monoidal category of concurrent processes in Idris.
Still very experimental. Run the example.
This library helps in defining systems of concurrent communicating processes. The processes are structured as morphisms in a symmetric monoidal category; see the nLab for an explanation of what those are.
-
The objects are vectors of oriented typed wires, which are types decorated with one of the orientations
Up
orDown
. The monoidal structure on the objects is simply vector concatenation:++
. A wire represents a channel of communication between two processes and the orientation indicates the direction in which data flows. -
The morphisms
[u_0, ..., u_m] -> [w_0, ..., w_n]
in the category are represented by concurrent process which haveu_0
, ...,u_m
as top wires andw_0
, ...,w_n
as bottom wires. On both the top and bottom a process may have input and output wires, indicated by the orientation of those wires. The type of processes is given by the dependent typeHom [u_0, ..., u_m] [w_0, ..., w_n].
-
Processes
f : Hom as bs
andg : Hom bs cs
may be composed, producingf -*- g : Hom as cs
, and this makes a category. This is acheived by joining the appropriate communication channels betweenf
andg
. -
There is also a monoidal structure producing
f + g : Hom (as ++ as') (bs ++ bs')
whenever
f : Hom as bs
g : Hom as' bs'
This runs the processes f
and g
in parallel. This makes the category of processes a symmetric monoidal category, because there is also have a special process:
sym: Hom (as ++ bs) (bs ++ as).
Because we get a symetric monoidal category, we reason about the processes using string diagrams.
The library defines some helper functions for defining processes. One is:
mkPure: (name: String) -> (f: a -> b) -> Hom [Down a] [Down b]
Given a pure function f: a -> b
, this produces a process
│
↓ a <-- downwards input wire of type a
│
┌─┴─┐
│ f │
└─┬─┘
│
↓ b <-- downwards output wire of type b
│
There is also upPrinter
and downPrinter
:
│ ┌───────┐
↓ a │ print │
│ └───┬───┘
┌───┴───┐ │
│ print │ ↑ a
└───────┘ │
downPrinter upPrinter
Note that one can be obtained from the other using the dualise
function:
dualise: Hom as bs -> Hom (dual bs) (dual as)
where the dual of a typed wire is the same wire going in the other direction.
We also have cap
and cup
which are the unit and co-unit respectively:
↓ ↑
│ │
└────┘
cup : Hom [Down a, Up a] []
┌────┐
│ │
↓ ↑
cap : Hom [Down a, Up a] []
With these we can already produce a non-trivial composite process (See Examples.idr
):
myProc: Hom [Down Int] []
myProc = downIntWire + cap
-*- (splice -*- inc -*- gt5) + upIntWire
-*- downPrinter + cup
where:
gt5: Hom [Down Int] [Down Int, Down Int]
gt5 = mkPure "gt5: " (\n => if n > 5 then Left n else Right n) -.- splitEither
inc: Hom [Down Int] [Down Int]
inc = mkPure "inc: " (\i => i + 1)
downIntWire: Hom [Down Int] [Down Int]
downIntWire = mkPure "down int: " id
upIntWire: Hom [Up Int] [Up Int]
upIntWire = dualise downIntWire
This can be visualised as:
↓
│ ┌─────┐
└───┤ │
│ │
↓ ↑
│ │
┌──┴──┐ │
│ +1 │ │
└──┬──┘ │
│ │
↓ │
│ │
┌──┴──┐ │
│ > 5 │ │
└─┬─┬─┘ │
┌─┘ ↓ ↑
│ │ │
↓ └────┘
│
┌───┴───┐
│ print │
└───────┘
Which is a process that, when an integer is sent to it's only input wire, will keep incrementing that integer until it is greater than 6, and then it will print it.
- Install Idris: instructions.
- Download the repo:
git clone https://github.com/jameshaydon/smproc.git
- Start the idris repl:
cd src; idris -p contrib Examples.idr
(compilation will take a while) - Run main:
:exec main
- Make the abstract representation (started in
Bord.idr
). - Optimise the abstract representation using string diagram transformations/simplifications.
- Write the compiler:
abstract rep --> process
. - For the moment the topology is fixed at compile-time. Think about how this can be more flexible.
- Make a version for the javascript targer, maybe using js-csp.