ocaml-mpst is a communication library powered by Multiparty Session Types
(abbreviated as MPST) in OCaml. Thus it ensures:
- Protocol fidelity (communication will take place according to a prescribed protocol) and
- Communication safety (you do not get any type-mismatch errors)
--- under the assumption that all communication channels are used linearly.
Linearity is checked either dynamically (default) or statically, via another
Install OPAM. then the following command will install OCaml-MPST in your OPAM switch:
opam pin -y https://github.com/keigoi/ocaml-mpst.git
It will install several packages from this repository. To remove them, type:
opam pin remove concur-shims linocaml-light ocaml-mpst ocaml-mpst-lwt ocaml-mpst-plug ocaml-mpst-plug-http
For benchmarks and some examples, you might need additional dependencies, which can be installed by:
opam install dns-lwt-unix cmdliner lwt_log lwt_ssl core_bench conf-libev
- An interactive web interface is available at:
Try OCaml-MPST on your PC
To run a example, after installation, type the script below in the terminal:
git clone https://github.com/keigoi/ocaml-mpst.git cd ocaml-mpst/ # compile it dune build examples/mpst/ring.exe # run it dune exec examples/mpst/ring.exe
Or alternatively, you can also use
ocamlfind to compile things:
ocamlfind ocamlopt -thread -linkpkg -package ocaml-mpst ring.ml -o ring.exe ./ring.exe
ocaml-mpst in 5 minutes
- Write down a protocol using Global Combinators.
open Mpst let ring = gen @@ (a --> b) msg @@ (b --> c) msg @@ (c --> a) finish
--- This is a simple three-party ring-based protocol with participants
C, circulating messages with label
msg in this order.
(a --> b) msgspecifies a message with label
msgis sent from
b. Protocols are composed by applying combinator
-->to existing protocols (possibly via OCaml's function application operator
@@, as above).
finishdenotes termination of a protocol.
(More combinators will be explained later.)
- Extract channels for each participants (here
C) from the protocol:
let sa = get_ch a ring let sb = get_ch b ring let sc = get_ch c ring
- Run threads in parallel, one for each participant!
NB: The following uses concur-shims offers an
monad parameterised over direct style and LWT.
open Concur_shims let (let*) = IO.bind
(* Participant A *) Thread.create (fun () -> let* sa = send sa#role_B#msg "Hello, " in let* `msg(str, sa) = receive sa#role_C in print_endline str; close sa ) ();; (* Participant B *) Thread.create (fun () -> let* `msg(str,sb) = receive sb#role_A in let* sb = send sb#role_C#msg (str ^ "MPST") in close sb ) ();; (* Participant C *) let* `msg(str, sc) = receive sc#role_C in let* sc = send sc#role_A#msg (str ^ " World!") in close sc
It will start two threads behaving as the participant
B, then runs
in the main thread.
send s#role_X#msg valueoutputs on channel
X, with a message label
msgand payload value
s#role_X#msgis a standard method invocation syntax of OCaml, chained twice in a row. It returns a continuation channel which should be re-bound to the same variable
sensuring linearity, which is why sending is written as
let s = send s#roleX .. in.
receive s#role_Winputs the message from role
W. The received message will have form
msg(val, s)` packed inside a OCaml's _polymorphic variant_ constructormsg
, with payload valueval
and continuation channels
(again, re-binding existing channel variables`).
closeterminates a communication channel.
The above code is session-typed, as prescribed in the protocol
The all communications are deadlock-free, faithful to the protocol, and
Some basic notes:
- In a protocol
(x --> y) msg @@ cont,
-->is a 4-ary operator taking an output role
x, input role
y, message label
@@is a function application operator (equivalent to
- Output expression
send s#role_X#msg valueis parsed as
((send (s#role_X#msg)) value).
More examples including branching, loops and delegation will come soon!
- See Examples
Benchmark Scripts run_all.sh depends on fixed OPAM
ocaml-mpst-lwt. Please install the corresponding
packages, for example:
opam switch create ocaml-mpst-lwt 4.09.1+flambda opam pin -y https://github.com/keigoi/ocaml-mpst.git # install additional dependencies opam install dns-lwt-unix cmdliner lwt_log lwt_ssl core_bench conf-libev opam switch create ocaml-mpst-ev 4.09.1+flambda git clone https://github.com/keigoi/ocaml-mpst.git cd ocaml-mpst opam pin -n -y . cd packages/ocaml-mpst-ev opam pin -y
Notes on optional library dependencies
LWT vs. Threads
Thus, the programming interface and behaviour significantly changes if you (un)install LWT.
- Output is non-blocking in LWT version of OCaml-MPST.
- Output is blocking in
threadsversion (it uses
Normally, you can just use
lwt version of the OCaml-MPST.
Also note that the above Quick Start automatically installs LWT, as
ocaml-mpst-plug-http indirectly depends on
(The reasons for using
concur_shims are (1) to ease readability and
maintainability of the implementation code (2) running benchmark using both
lwt and (3) to provide an easy interface for users who are not
fluent with recent OCaml based on
Monadic vs. Direct Style
Some programming idioms in direct style is not available in lwt. While you can use let rebinding in OCaml 4.08.0 or later, like
let (let*) = Lwt.bind (* IO.bind in concur-shims will also work *) ... let* `msg(x, s) = receive s#role_A in ... (* legal *)
You can't do the same thing in
match* receive s#role_A with (* illegal *) | `left(x, s) -> ... | `right(x, s) -> ...
In that case, you must bind the received result once and match on it, like the following:
let* var = receive s#role_A in match var with | `left(x, s) -> ... | `right(x, s) -> ...
Another solution would be to use ppx_let.
Nano_mutex in JaneStreet Core
Nano_mutex in Jane Street's Core makes better performance in dynamic linearity
checking. If you install
core, OPAM will automatically recompiles everything.
Otherwise, OCaml-MPST uses either
Lwt_mutex module (indirectly via