Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
lib
 
 
lwt
 
 
 
 
 
 
 
 
 
 
 
 
 
 

OCaml-MPST

What is ocaml-mpst?

ocaml-mpst is a communication library powered by Multiparty Session Types (abbreviated as MPST) in OCaml. Thus it ensures:

  • Deadlock-freedom,
  • 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 library linocaml.

Install

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

Try OCaml-MPST Online!

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

  1. 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 A, B and C, circulating messages with label msg in this order.

  • Protocol (a --> b) msg specifies a message with label msg is sent from a to b. Protocols are composed by applying combinator --> to existing protocols (possibly via OCaml's function application operator @@, as above).
  • Combinator finish denotes termination of a protocol.

(More combinators will be explained later.)

  1. Extract channels for each participants (here sa for A, sb for B, and sc for C) from the protocol:
let sa = get_ch a ring
let sb = get_ch b ring
let sc = get_ch c ring
  1. Run threads in parallel, one for each participant!

NB: The following uses concur-shims offers an IO 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 A and B, then runs C in the main thread.

  • Primitive send s#role_X#msg value outputs on channel s to role X, with a message label msg and payload value value. Expression s#role_X#msg is 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 s ensuring linearity, which is why sending is written as let s = send s#roleX .. in .
  • Primitive receive s#role_W inputs the message from role W. The received message will have form msg(val, s)` packed inside a OCaml's _polymorphic variant_ constructor msg, with payload value valand continuation channels(again, re-binding existing channel variables`).
  • Primitive close terminates a communication channel.

The above code is session-typed, as prescribed in the protocol ring above. The all communications are deadlock-free, faithful to the protocol, and type-error free!

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 msg and continuation cont, where @@ is a function application operator (equivalent to $ in Haskell).
  • Output expression send s#role_X#msg value is parsed as ((send (s#role_X#msg)) value).

More examples including branching, loops and delegation will come soon!

Presentation

Examples

Benchmark Scripts

Benchmark Scripts run_all.sh depends on fixed OPAM switches: ocaml-mpst-ev and 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

OCaml-MPST depends on concur-shims, which provides a thin layer for switching between standard threads library and LWT.

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 threads version (it uses Event.channel).

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 lwt.

(The reasons for using concur_shims are (1) to ease readability and maintainability of the implementation code (2) running benchmark using both threads and lwt and (3) to provide an easy interface for users who are not fluent with recent OCaml based on lwt.)

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-construct:

  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 Mutex or Lwt_mutex module (indirectly via concur_shims).

About

Multiparty Session Types in OCaml

Resources

Releases

No releases published

Packages

No packages published