- The document discusses global protocol combinators for specifying multiparty session types in OCaml.
- It presents an approach to ensure deadlock-freedom solely through type checking in OCaml, without code generation. Local types are inferred from the global protocol specification.
- Well-formedness of the global protocol and type checking of the OCaml program ensure deadlock-freedom. Branching and non-determinism in protocols can be detected as type errors.
1. Keigo Imai1
(with Rumyana Neykova2, Nobuko Yoshida3 and Shoji Yuen4)
1Gifu University (JP) 2Brunel University London (UK)
3Imperial College London (UK) 4Nagoya University (JP)
PLAS Group Seminar, University of Kent
16th Sept. 2019
1
Global Protocol Combinators:
Static Structural Multiparty Sessions Over
Simply Typed Channels
in OCaml
3. Deadlocks in message-passing concurrency: Protocols prevent them!
3
A
B C
A ring-form
multiparty
communication:
Concurrent
programming is difficult!
4. Deadlocks in message-passing concurrency: Protocols prevent them!
4
A
B C
A ring-form
multiparty
communication:
Concurrent
programming is difficult!
C
wrong usage (direction)…
② input① output
order matters…
5. Deadlocks in message-passing concurrency: Protocols prevent them!
5
A
B C
A ring-form
multiparty
communication:
Concurrent
programming is difficult!
A B C
Communication protocol!
C
wrong usage (direction)…
② input① output
order matters…
6. Deadlocks in message-passing concurrency: Protocols prevent them!
6
A
B C
A ring-form
multiparty
communication:
Concurrent
programming is difficult!
A B C
Communication protocol!
C
wrong usage (direction)…
② input① output
order matters…
Best practice:
1. Design a communication protocol and check it
2. Focus on its local behaviours
3. Enforce protocol in the program code
7. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
7
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
[Honda et al., 2008]
8. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
8
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
"well-formed": protocol G iswf(G) ✔
G = C → S. S → C.end
deadlock-free
[Honda et al., 2008]
9. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
9
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
"well-formed": protocol G iswf(G) ✔
G = C → S. S → C.end
deadlock-free
G↾C = LC = LSG↾S
S! S?
C? C!
LC =
LS =
: "views" of the protocol
[Honda et al., 2008]
10. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
10
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
✔✔ : "these processes respect the protocol"PSPC LS ⊢LC ⊢
"well-formed": protocol G iswf(G) ✔
G = C → S. S → C.end
deadlock-free
G↾C = LC = LSG↾S
S! S?
C? C!
LC =
LS =
: "views" of the protocol
[Honda et al., 2008]
11. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
11
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
✔✔ : "these processes respect the protocol"PSPC LS ⊢LC ⊢
"well-formed": protocol G iswf(G) ✔
G = C → S. S → C.end
deadlock-free
G↾C = LC = LSG↾S
S! S?
C? C!
LC =
LS =
: "views" of the protocol
deadlock-free!
[Honda et al., 2008]
12. deadlock-free?
Multiparty Session Types (MPSTs): Protocol-based deadlock freedom
12
C S
a "ping" protocol
③ Give concurrent processes (programs) and check their types
① Write a Global Type and check it
② Derive Local Types by End Point Projection (↾)
PC | PS
The system PC | PS is
✔✔ : "these processes respect the protocol"PSPC LS ⊢LC ⊢
"well-formed": protocol G iswf(G) ✔
G = C → S. S → C.end
deadlock-free
G↾C = LC = LSG↾S
S! S?
C? C!
LC =
LS =
: "views" of the protocol
deadlock-free!
[Honda et al., 2008]
How to implement MPSTs
in a programming language?
Question
13. ③ LC ⊢ PC ?
Go
Thread
State of the art: Deadlock freedom via code generation
13
① wf(G) ?
② G↾C
Global
Protocol File
Go
Thread
Go
Thread
In a MPST toolchain for programming language Go [D. Castro et al., POPL 2019]:
14. ③ LC ⊢ PC ?
Go
Thread
State of the art: Deadlock freedom via code generation
14
① wf(G) ?
② G↾C
Global
Protocol File
Go
Thread
Go
Thread
In a MPST toolchain for programming language Go [D. Castro et al., POPL 2019]:
deadlock-free
(Well-formedness checking
and End Point Projection)
Go type
(Generated
code)
Go type
(Generated
code)
Go type
(Generated
code)
Code
generation
✔
15. ③ LC ⊢ PC ?
Type checking
by
Go compiler
✔ (type checking to check
protocol conformance)
Go
Thread
State of the art: Deadlock freedom via code generation
15
① wf(G) ?
② G↾C
Global
Protocol File
Go
Thread
Go
Thread
In a MPST toolchain for programming language Go [D. Castro et al., POPL 2019]:
deadlock-free
(Well-formedness checking
and End Point Projection)
Go type
(Generated
code)
Go type
(Generated
code)
Go type
(Generated
code)
Code
generation
✔
16. ③ LC ⊢ PC ?
Type checking
by
Go compiler
✔ (type checking to check
protocol conformance)
Go
Thread
State of the art: Deadlock freedom via code generation
16
① wf(G) ?
② G↾C
Global
Protocol File
Go
Thread
Go
Thread
In a MPST toolchain for programming language Go [D. Castro et al., POPL 2019]:
deadlock-free
(Well-formedness checking
and End Point Projection)
Go type
(Generated
code)
Go type
(Generated
code)
Go type
(Generated
code)
Code
generation
✔
deadlock-free
17. ③ LC ⊢ PC ?
Type checking
by
Go compiler
✔ (type checking to check
protocol conformance)
Go
Thread
State of the art: Deadlock freedom via code generation
17
① wf(G) ?
② G↾C
Global
Protocol File
Go
Thread
Go
Thread
In a MPST toolchain for programming language Go [D. Castro et al., POPL 2019]:
deadlock-free
(Well-formedness checking
and End Point Projection)
Go type
(Generated
code)
Go type
(Generated
code)
Go type
(Generated
code)
Code
generation
✔
deadlock-free
Is it possible to implement MPSTs
without code generation?
Question
18. ③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
18
Yes (!) How?
19. ↓ A single OCaml program code file (.ml)
OCaml
Thread
OCaml
Thread
OCaml
Thread
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
19
Yes (!) How?
20. ↓ A single OCaml program code file (.ml)
OCaml
Thread
OCaml
Thread
OCaml
Thread
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
20
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking &
inference
Yes (!) How?
21. ↓ A single OCaml program code file (.ml)
OCaml
Thread
OCaml
Thread
OCaml
Thread
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
21
Type checking
by
OCaml compiler
✔
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking &
inference
Yes (!) How?
22. ↓ A single OCaml program code file (.ml)
OCaml
Thread
OCaml
Thread
OCaml
Thread
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
22
Type checking
by
OCaml compiler
✔
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking &
inference
Yes (!)
deadlock-free!
How?
23. ↓ A single OCaml program code file (.ml)
OCaml
Thread
OCaml
Thread
OCaml
Thread
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom solely by Types!
23
Type checking
by
OCaml compiler
✔
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking &
inference
Yes (!)
deadlock-free!
How?
How/why do they work??
24. Global Combinators and well-formedness check
24
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
25. Global Combinators and well-formedness check
25
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
26. Global Combinators and well-formedness check
26
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
27. Global Combinators and well-formedness check
27
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))
choice_at r g1 g2 : decide a protocol branching at r, between g1 and g2
Example: Adding a branch to Simple OAuth protocol
(a few plumbings needed to convince the type checker)
Each branch must begin from s
28. Global Combinators and well-formedness check
28
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))
choice_at r g1 g2 : decide a protocol branching at r, between g1 and g2
NB: Branching in a protocol is a source of concurrency issues
(a few plumbings needed to convince the type checker)
29. Global Combinators and well-formedness check
29
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))
choice_at r g1 g2 : decide a protocol branching at r, between g1 and g2
NB: Branching in a protocol is a source of concurrency issues
(a few plumbings needed to convince the type checker)
Non-determinism/
Deadlock
in a protocol
a
30. Global Combinators and well-formedness check
30
① wf(G) ✔
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
(r1 --> r2) lab : send a label lab from r1 to r2 (with possible payloads)
Example: Simple OAuth protocol
s c a
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))
choice_at r g1 g2 : decide a protocol branching at r, between g1 and g2
NB: Branching in a protocol is a source of concurrency issues
(a few plumbings needed to convince the type checker)
… is reported as a type error
Non-determinism/
Deadlock
in a protocol
a
31. (Deadlock and type errors: An analysis)
31
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))a
poke a instead of c
The MPST theory rejects it: they are not "mergeable".
Forming a "mixed choice" (which causes nondeterminism)
c's first action is input from s
c's first action is output to a
32. Deriving local types via type inference
OCaml infers Local Types from global combinators.
32
② G↾C
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
Example (again): Simple OAuth protocol
s c a
All local types are inferred at once, as a cons-list of structural types (here, s=0, c=1, a=2):
33. Deriving local types via type inference
OCaml infers Local Types from global combinators.
33
② G↾C
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
Example (again): Simple OAuth protocol
s c a
All local types are inferred at once, as a cons-list of structural types (here, s=0, c=1, a=2):
val oauth :
[ `cons of
< role_C : < login : ('a * close) out lin > > *
[ `cons of
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin > *
[ `cons of
< role_C : [> `password of 'b *
< role_C : < auth : ('c *
close) out lin > > ] inp lin > *
([ `cons of close * 'x ] as 'x) ] ] ] Seq.t
c!login
s?login a!pass a?auth
s!pass s?auth
s
c
a
34. Deriving local types via type inference
OCaml infers Local Types from global combinators.
34
② G↾C
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
Example (again): Simple OAuth protocol
s c a
All local types are inferred at once, as a cons-list of structural types (here, s=0, c=1, a=2):
val oauth :
[ `cons of
< role_C : < login : ('a * close) out lin > > *
[ `cons of
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin > *
[ `cons of
< role_C : [> `password of 'b *
< role_C : < auth : ('c *
close) out lin > > ] inp lin > *
([ `cons of close * 'x ] as 'x) ] ] ] Seq.t
c!login
s?login a!pass a?auth
s!pass s?auth
s
c
a
Destination role's names are a method of an object
< role_S
< role_A
< role_A
35. Deriving local types via type inference
OCaml infers Local Types from global combinators.
35
② G↾C
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
Example (again): Simple OAuth protocol
s c a
All local types are inferred at once, as a cons-list of structural types (here, s=0, c=1, a=2):
val oauth :
[ `cons of
< role_C : < login : ('a * close) out lin > > *
[ `cons of
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin > *
[ `cons of
< role_C : [> `password of 'b *
< role_C : < auth : ('c *
close) out lin > > ] inp lin > *
([ `cons of close * 'x ] as 'x) ] ] ] Seq.t
c!login
s?login a!pass a?auth
s!pass s?auth
s
c
a
Output labels are methods in an object
< password
Destination role's names are a method of an object
< role_S
< role_A
< role_A
36. Deriving local types via type inference
OCaml infers Local Types from global combinators.
36
② G↾C
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
Example (again): Simple OAuth protocol
s c a
All local types are inferred at once, as a cons-list of structural types (here, s=0, c=1, a=2):
val oauth :
[ `cons of
< role_C : < login : ('a * close) out lin > > *
[ `cons of
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin > *
[ `cons of
< role_C : [> `password of 'b *
< role_C : < auth : ('c *
close) out lin > > ] inp lin > *
([ `cons of close * 'x ] as 'x) ] ] ] Seq.t
c!login
s?login a!pass a?auth
s!pass s?auth
s
c
a
Input labels are (polymorphic) variant type tags
[ `login
[ `auth
Output labels are methods in an object
< password
Destination role's names are a method of an object
< role_S
< role_A
< role_A
37. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
37
③ LC ⊢ PC ✔
38. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
38
< role_S
Select destination role via method invocation (#)
role_S
③ LC ⊢ PC ✔
39. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
39
< role_S
Select destination role via method invocation (#)
role_S
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
40. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
40
< role_S
Select destination role via method invocation (#)
role_S
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
"Next" channel is supplied as a part of the returned value
41. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
41
< role_S
Select destination role via method invocation (#)
role_S
< role_A
role_A
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
"Next" channel is supplied as a part of the returned value
42. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
42
< role_S
Select destination role via method invocation (#)
role_S
< role_A
role_A
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
"Next" channel is supplied as a part of the returned value
< password
#password
Select an output label via method invocation (#), then
send it with a payload
43. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
43
< role_S
Select destination role via method invocation (#)
role_S
< role_A
role_A
role_A
< role_A
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
"Next" channel is supplied as a part of the returned value
< password
#password
Select an output label via method invocation (#), then
send it with a payload
44. let ch = get_ch c oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
Protocol conformance via OCaml's type checking
44
< role_S
Select destination role via method invocation (#)
role_S
< role_A
role_A
`auth
[ `auth
role_A
< role_A
③ LC ⊢ PC ✔
[ `login
`login
… and receive on it, then pattern-match the value against variant tags (labels)
received payload
"Next" channel is supplied as a part of the returned value
< password
#password
Select an output label via method invocation (#), then
send it with a payload
45. let ch = get_ch s oauth (* get a MPST channel for s *)
val ch :
< role_S : [> `login of 'a *
< role_A : < password : ('b *
< role_A : [> `auth of 'c *
close ] inp lin >) out lin > > ] inp lin >
OCaml>
let oauth = (s --> c) login ((c --> a) password ((a --> c) auth finish))
let thread_C () =
match receive ch#role_S with
| `login((u:string), ch)->
let ch = send ch#role_A#password "asdf" in
match receive ch#role_A with
| `auth((b:bool), ch) ->
close ch
45
Protocol conformance via OCaml's type checking③ LC ⊢ PC ✔
ro
If there is a
typo …
OCaml reports a type error at this point
46. ↓ A single OCaml Source code file (.ml)
OCaml
Program
OCaml
Program
OCaml
Program
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom by Types!
46
Type checking
by
OCaml compiler
✔
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking
& inference
Recap
deadlock-free!
47. ↓ A single OCaml Source code file (.ml)
OCaml
Program
OCaml
Program
OCaml
Program
Protocol
in OCaml
③ LC ⊢ PC ?
① wf(G) ?
② G↾C
Global Protocol Combinator : Deadlock freedom by Types!
47
Type checking
by
OCaml compiler
✔
✔
deadlock-free
OCaml types OCaml types OCaml types
Type
checking
& inference
Recap
deadlock-free!
Are they really correct??
48. GPC: Are they correct
48
A. Key idea: Global Combinators pre-generate channels
❖ Multiparty channels as nested sequence of simply-typed channels
B. Formalisation of GPC with Deadlock-freedom Theorem
❖ OCamlM: A minimal concurrent language
❖ Typing rules for Global Combinators
C. Implementation: GPC as a typed DSL in OCaml
❖ Based on a set of type manipulation techniques
(GADTs, field label encoding, and plumbings such as polymorphic record extension)
GPC key ingredients
49. GPC: Are they correct
49
A. Key idea: Global Combinators pre-generate channels
❖ Multiparty channels as nested sequence of simply-typed channels
B. Formalisation of GPC with Deadlock-freedom Theorem
❖ OCamlM: A minimal concurrent language
❖ Typing rules for Global Combinators
C. Implementation: GPC as a typed DSL in OCaml
❖ Based on a set of type manipulation techniques
(GADTs, label encoding, and plumbings such as polymorphic record extension)
GPC key ingredients
51. A. Key idea: Global Combinators pre-generate channels
51
S
C A
⟨⟩ ⟨⟩
⟨⟩
let oAuth = finish
52. A. Key idea: Global Combinators pre-generate channels
52
S
C A
Sauth
⟨?Sauth⟩ ⟨!Sauth⟩
⟨⟩
let oAuth = (A → C) auth finish
53. A. Key idea: Global Combinators pre-generate channels
53
let oAuth = (C → A) password ((A → C) auth finish)
S
C A
Sauth
Spasswd
⟨!Spasswd; ?Sauth⟩ ⟨?Spasswd; !Sauth⟩
⟨⟩
54. A. Key idea: Global Combinators pre-generate channels
54
let oAuth = (S → C) login ((C → A) password ((A → C) auth finish))
S
C
A
⟨?Slogin ; !Spasswd; ?Sauth.0 ⟩ ⟨?Spasswd; !Sauth⟩
Sauth
Spasswd
Slogin
⟨!Slogin ⟩
55. A. Key idea: Global Combinators pre-generate channels
55
let oAuth = (S → C) login ((C → A) password ((A → C) auth finish))
S
C
A
⟨?Slogin ; !Spasswd; ?Sauth.0 ⟩ ⟨?Spasswd; !Sauth⟩
Sauth
Spasswd
Slogin
⟨!Slogin ⟩
56. Distribute them and start the communication
56
PS
PC
PA
!Slogin.0| ?Slogin ; !Spasswd; ?Sauth.0| ?Spasswd;!Sauth.0
57. Distribute them and start the communication
57
PS
PC
PA
0| !Spasswd; ?Sauth.0| ?Spasswd;!Sauth.0
58. Distribute them and start the communication
58
PS
PC
PA
0| ?Sauth.0| !Sauth.0
60. Distribute them and start the communication
60
PS
PC
PA
!Slogin.0| ?Slogin ; !Spasswd; ?Sauth.0| ?Spasswd;!Sauth.0
❖ All these channels are simply-typed (hence no type-errors)
❖ Global combinators aim to be deadlock-free
❖ The nested structure enforces usage order
Point
61. GPC: Are they correct? (cont.)
61
A. Key idea: Global Combinators pre-generate channels
❖ Multiparty channels as nested sequence of simply-typed channels
B. Formalisation of GPC (with Deadlock-freedom Theorem)
❖ OCamlM: A minimal concurrent language
❖ Typing rules for Global Combinators (with hints to OCaml DSL)
❖ Session Type merging
C. Implementation: GPC as a typed DSL in OCaml
❖ Based on a set of type manipulation techniques
(GADTs, field label encoding, and plumbings such as polymorphic record extension)
GPC key ingredients
62. B. OCamlM: Formalisation of GPC with Deadlock-freedom Theorem
• A minimal concurrent language with structural subtyping,
equi-recursive types and simply-typed channels
• Typing rules for global combinators
62
63. Typing rules
63
p1 pn
… … …
Point: give an index for each role (and order roles)
wf(G) ✔, G↾C
64. The typing rule for (-->)
64
p1 pn
…
pi pj
… …
wf(G) ✔, G↾C
i
65. The typing rule for (-->)
65
p1 pn
…
pi pj
… …
Index-based type update
wf(G) ✔, G↾C
i
66. The typing rule for (-->)
66
p1 pn
…
pi pj
… …
Index-based type update
p1 pn
…
pi pj
… …
"Wrap" with opponent's role names and labels
wf(G) ✔, G↾C
i
67. The typing rule for (-->)
67
p1 pn
…
pi pj
… …
Index-based type update
p1 pn
…
pi pj
… …
"Wrap" with opponent's role names and labels
Implement them by using GADTs and
polymorphic lens (explained later)
available in OCaml
wf(G) ✔, G↾C
i
68. The typing rule for (-->)
68
p1 pn
…
pi pj
… …
Index-based type update
p1 pn
…
pi pj
… …
"Wrap" with opponent's role names and labels
Requires first-class record fields
(methods) and variant tags
⇒ Encode them as values
encode it in OCaml
Implement them by using GADTs and
polymorphic lens (explained later)
available in OCaml
wf(G) ✔, G↾C
i
70. The typing rule for branching (choice_at)
70
Ensure that pa is a sending
wf(G) ✔, G↾C
71. The typing rule for branching (choice_at)
71
Ensure that pa is a sending
Output labels must be disjoint (deterministic)
wf(G) ✔, G↾C
72. The typing rule for branching (choice_at)
72
Ensure that pa is a sending
Output labels must be disjoint (deterministic)
Merge two records (objects) into one
wf(G) ✔, G↾C
73. The typing rule for branching (choice_at)
73
Ensure that pa is a sending
Output labels must be disjoint (deterministic)
Merge two records (objects) into one
Encode disjoint record merging
encode it in OCaml
wf(G) ✔, G↾C
74. The typing rule for branching (choice_at)
74
wf(G) ✔, G↾C
Too restrictive for communication protocols
Solution: Subtyping!
Requires each role having the same type (behaviour) in each branch
→ seems standard wrt. if-then-else construct, but
75. The typing rule for branching (choice_at)
75
wf(G) ✔, G↾C
Too restrictive for communication protocols
Solution: Subtyping!
Row polymorphism
(objects and polymorphic variants)
available in OCaml
Requires each role having the same type (behaviour) in each branch
→ seems standard wrt. if-then-else construct, but
76. Typing rules
76
…
p1 pn
…
pi
…
p1 pn
…
pi
…
p1 pn
…
pi
…
Γ ⊢ choice … {g1; g2} : ⋯×p&{m1:T1, m2:T2}×⋯
Γ ⊢ g1 : ⋯×p&{m1:T1}×⋯ Γ ⊢ g2 : ⋯×p&{m2:T2}×⋯
Desired type
(According to Full Merging in MPST)
A problem:
p&{m1:T1} ≠ p&{m2:T2}
79. Deadlock and type errors: An analysis
79
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))a
poke a instead of c
The MPST theory rejects it: they are not "mergeable".
Forming a "mixed choice" (which causes nondeterminism)
80. Deadlock and type errors: An analysis
80
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))a
poke a instead of c
The MPST theory rejects it: they are not "mergeable".
Forming a "mixed choice" (which causes nondeterminism)
c's first action is output to a <a: <quit: !bool*cont2>>
81. Deadlock and type errors: An analysis
81
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))a
poke a instead of c
The MPST theory rejects it: they are not "mergeable".
Forming a "mixed choice" (which causes nondeterminism)
c's first action is input from s <s: ?[login_str*cont1]>
c's first action is output to a <a: <quit: !bool*cont2>>
82. Deadlock and type errors: An analysis
82
let oauth_branch =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login ((c --> a) password ((a --> c) auth finish)))
(s, (s --> c) cancel ((c --> a) quit finish))a
poke a instead of c
The MPST theory rejects it: they are not "mergeable".
Forming a "mixed choice" (which causes nondeterminism)
c's first action is input from s <s: ?[login_str*cont1]>
c's first action is output to a <a: <quit: !bool*cont2>>
No least upper bound (common supertype)
⇒ type error
No least upper bound (common supertype)
⇒ type error
83. A note on typing
83
<r: ?[lab: T1*T2]>
<r: <lab: !T1*T2>>
Input and output session types are not symmetric with each other. Why?
sok : ?bool, scancel : ?int
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
Making a multiplexed channel from
simply-typed channels
84. A note on typing
84
<r: ?[lab: T1*T2]>
<r: <lab: !T1*T2>>
Input and output session types are not symmetric with each other. Why?
sok : ?bool, scancel : ?int
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
Concurrent ML (Reppy, 1993)
Types are slightly modified, they uses "events" in reality
Making a multiplexed channel from
simply-typed channels
85. A note on typing
85
<r: ?[lab: T1*T2]>
<r: <lab: !T1*T2>>
Input and output session types are not symmetric with each other. Why?
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
Concurrent ML (Reppy, 1993)
Types are slightly modified, they uses "events" in reality
Making a multiplexed channel from
simply-typed channels
86. A note on typing
86
<r: ?[lab: T1*T2]>
<r: <lab: !T1*T2>>
Input and output session types are not symmetric with each other. Why?
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel)
: List[ ?[ok_(boolו), cancel_(intׅ)] ]
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
Concurrent ML (Reppy, 1993)
Types are slightly modified, they uses "events" in reality
Making a multiplexed channel from
simply-typed channels
87. A note on typing
87
<r: ?[lab: T1*T2]>
<r: <lab: !T1*T2>>
Input and output session types are not symmetric with each other. Why?
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel)
: List[ ?[ok_(boolו), cancel_(intׅ)] ]
choose (List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel))
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
Concurrent ML (Reppy, 1993)
Types are slightly modified, they uses "events" in reality
Making a multiplexed channel from
simply-typed channels
88. GPC: Are they correct? (cont.)
88
A. Key idea: Global Combinators pre-generate channels
❖ Multiparty channels as nested sequence of simply-typed channels
B. Formalisation of GPC (with Deadlock-freedom Theorem)
❖ OCamlM: A minimal concurrent language
❖ Typing rules for Global Combinators (with hints to OCaml DSL)
❖ Session Type merging
C. Implementation: GPC as a typed DSL in OCaml
❖ Based on a set of type manipulation techniques
(GADTs, field label encoding, and details of plumbings for polymorphic record extension)
❖ Benchmarks: Garbage collection matters
❖ Linearity monad
GPC key ingredients
Please ask me over beer!
89. GPC wrap up
89
Global Protocol Combinator: a DSL for writing global protocols
❖ Key Ingredients: Global Combinators
❖ Covers all basic MPST features (including full merging)
❖ Extensions: HTTP, and OAuth Example (on top of it), and more
OCamlM: fomalisation of a minimal programming language for GPC
❖ Subject Reduction, Operational Correspondence and Deadlock-freedom
❖ Types for global combinators
❖ Merging is a least upper bound in subtyping relation
Sauth
Spasswd
let oauth = (s --> c) login …
p1 pn
… … …
90. Future Work
90
A → B
B → C
A → B
• Deadlock-freedom proof on full global combinators
❖ Including more expressive ones (e.g. relaxed loop conditions)
❖ Scatter/Gather (broadcast), undirected internal choice, …
• Implementation in other programming languages
❖ TypeScript (structural subtyping from beginning, and will have recursive types as well)
❖ Haskell (automatically construct global combinators via type classes)
Thank you!
92. Loops are easy (for now)
92
let oAuth = (S → C) login ((C → A) password ((A → C) auth finish))
S
C
A
⟨?Slogin ; !Spasswd; ?Sauth.0 ⟩ ⟨?Spasswd; !Sauth⟩
Sauth
Spasswd
Slogin
⟨?Slogin ⟩
93. Loops are easy (for now)
93
let oAuth = fix (λx. (S → C) login ((C → A) password ((A → C) auth x))
S
C
A
μx.⟨?Slogin ; !Spasswd; ?Sauth; x ⟩
μx. ⟨?Spasswd; !Sauth; x⟩
Sauth
Spasswd
Slogin
μx. ⟨?Slogin; x⟩
95. Input / output primitives and row-polymorphism in OCaml
95
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
(FP's type inference-friendly structural typing)
96. Input / output primitives and row-polymorphism in OCaml
96
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
97. Input / output primitives and row-polymorphism in OCaml
97
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
Output labels are methods in
an object (fields in a polymorphic record)
Output labels are methods in
an object (fields in a polymorphic record)
98. Input / output primitives and row-polymorphism in OCaml
98
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
OCaml's row-polymorphism simulates session-type subtyping
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
Output labels are methods in
an object (fields in a polymorphic record)
Output labels are methods in
an object (fields in a polymorphic record)
99. Input / output primitives and row-polymorphism in OCaml
99
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
OCaml's row-polymorphism simulates session-type subtyping
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
Output labels are methods in
an object (fields in a polymorphic record)
Output labels are methods in
an object (fields in a polymorphic record)
val ch :
< role_C : [< `password of
string *
< role_C : < auth : (bool * close) out lin; .. >; .. >
] inp lin; .. >
-> unit
100. Input / output primitives and row-polymorphism in OCaml
100
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
OCaml's row-polymorphism simulates session-type subtyping
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
Output labels are methods in
an object (fields in a polymorphic record)
Output labels are methods in
an object (fields in a polymorphic record)
val ch :
< role_C : [< `password of
string *
< role_C : < auth : (bool * close) out lin; .. >; .. >
] inp lin; .. >
-> unit
Inferred polymorphic variant type: it can have less receiving labels
(= in this case it must have password at least since there must be one)
101. Input / output primitives and row-polymorphism in OCaml
101
let thread_A () =
let `password((p:string), ch) = receive ch#role_C in
let ch = send ch#role_C#auth true in
close ch
OCaml's row-polymorphism simulates session-type subtyping
(FP's type inference-friendly structural typing)
Input labels are
polymorphic variants
Input labels are
polymorphic variants
Output labels are methods in
an object (fields in a polymorphic record)
Output labels are methods in
an object (fields in a polymorphic record)
val ch :
< role_C : [< `password of
string *
< role_C : < auth : (bool * close) out lin; .. >; .. >
] inp lin; .. >
-> unit
Inferred polymorphic variant type: it can have less receiving labels
(= in this case it must have password at least since there must be one)
Inferred object type: it can have more sending labels
102. Branching
102
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> c) cancel @@ (c --> a) quit finish)
A protocol with branching:
let thread_C () =
match receive ch#role_S with
| `cancel((code:int), ch) -> close (send chc#role_A#quit ())
| `login((), ch) ->
let ch = send ch#role_A#password "asdf" in
let `auth(b, ch) = receive ch#role_A in
close ch
Code for C (waiting for S with an external choice):
103. Branching
103
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> c) cancel @@ (c --> a) quit finish)
A protocol with branching:
S will decide a branch, then C and A will follow
let thread_C () =
match receive ch#role_S with
| `cancel((code:int), ch) -> close (send chc#role_A#quit ())
| `login((), ch) ->
let ch = send ch#role_A#password "asdf" in
let `auth(b, ch) = receive ch#role_A in
close ch
Code for C (waiting for S with an external choice):
104. Branching
104
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> c) cancel @@ (c --> a) quit finish)
A protocol with branching:
S will decide a branch, then C and A will follow
let thread_C () =
match receive ch#role_S with
| `cancel((code:int), ch) -> close (send chc#role_A#quit ())
| `login((), ch) ->
let ch = send ch#role_A#password "asdf" in
let `auth(b, ch) = receive ch#role_A in
close ch
Code for C (waiting for S with an external choice):An external choice is a pattern matching
(idiomatic OCaml)
105. Loops and delegations (1)
105
let oauth2 =
fix (fun self ->
choice_at s (to_c login_cancel_or_retry)
(s, oauth1 ())
(s, (s --> c) retry @@ (c --> a) retry self))
Thanks to OCaml's equi-recursive types (built in polymorphic variants and objects by default),
programmers can handle recursions very naturally:
106. Loops and delegations (1)
106
let oauth2 =
fix (fun self ->
choice_at s (to_c login_cancel_or_retry)
(s, oauth1 ())
(s, (s --> c) retry @@ (c --> a) retry self))
recursionrecursion
Thanks to OCaml's equi-recursive types (built in polymorphic variants and objects by default),
programmers can handle recursions very naturally:
107. Loops and delegations (1)
107
let oauth2 =
fix (fun self ->
choice_at s (to_c login_cancel_or_retry)
(s, oauth1 ())
(s, (s --> c) retry @@ (c --> a) retry self))
recursionrecursion
protocol reuse
Thanks to OCaml's equi-recursive types (built in polymorphic variants and objects by default),
programmers can handle recursions very naturally:
108. Loops and delegations (1)
108
let oauth2 =
fix (fun self ->
choice_at s (to_c login_cancel_or_retry)
(s, oauth1 ())
(s, (s --> c) retry @@ (c --> a) retry self))
recursionrecursion
protocol reuse
val ch_S : < role_C : < cancel : ('_b * close) out lin;
login : ('_c * close) out lin;
retry : ('_d * 'a) out lin > > as 'a
OCaml>
Thanks to OCaml's equi-recursive types (built in polymorphic variants and objects by default),
programmers can handle recursions very naturally:
109. Loops and delegations (1)
109
let oauth2 =
fix (fun self ->
choice_at s (to_c login_cancel_or_retry)
(s, oauth1 ())
(s, (s --> c) retry @@ (c --> a) retry self))
recursionrecursion
protocol reuse
val ch_S : < role_C : < cancel : ('_b * close) out lin;
login : ('_c * close) out lin;
retry : ('_d * 'a) out lin > > as 'a
OCaml>
recursionrecursion
Thanks to OCaml's equi-recursive types (built in polymorphic variants and objects by default),
programmers can handle recursions very naturally:
110. Loops and delegations (2)
110
let oauth_deleg =
(mst --> wrk) (msg >: get_prot s oauth2)
finish
Delegation doesn't need any special treatment, since OCaml will just infer the delegated type.
However, you can annotate labels with a delegated protocol, like this:
val ch_Mst :
< role_Wrk : < msg :
(< role_C : < cancel : ('_b * close) out lin;
login : ('_c * close) out lin;
retry : ('_d * 'a) out lin > > as 'a *
close) out lin > >
OCaml>
Delegated type
111. Detecting protocol errors using types
111
Non-determinism/
Deadlock
in a protocol
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> a) cancel @@ (c --> a) quit finish)
NB: Branching is a source of concurrency issues
112. Detecting protocol errors using types
112
Non-determinism/
Deadlock
in a protocol
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> a) cancel @@ (c --> a) quit finish)
… is also reported as a type error
NB: Branching is a source of concurrency issues
113. Detecting protocol errors using types
113
Non-determinism/
Deadlock
in a protocol
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> a) cancel @@ (c --> a) quit finish)
… is also reported as a type error
Theorem: if a global type G is well-formed, its encoding
to a global combinator g =⟦G⟧ is well-typed in OCamlM.
NB: Branching is a source of concurrency issues
114. Detecting protocol errors using types
114
Non-determinism/
Deadlock
in a protocol
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> a) cancel @@ (c --> a) quit finish)
… is also reported as a type error
Theorem: if a global type G is well-formed, its encoding
to a global combinator g =⟦G⟧ is well-typed in OCamlM.
NB: Branching is a source of concurrency issues
Global Combinators have full expressivity of MPST Global Types
115. [Formalisation] OCamlM : a Minimal calculus for global combinators
Main results:
115
A calculus with structural subtyping, equi-recursive types and
i/o-typed channels
Theorem (Subject Reduction): if ⊢ e and e→* e' then ⊢ e'
Theorem (Operational Correspondence):
(Completeness)
(Soundness)
Corollary (Deadlock Freedom): If an MPST process P is
deadlock-free, OCamlM expression e=⟦P⟧ is deadlock-free.
116. Other features and Examples
✔ Branchings, loops, delegations
❖ Allows full merging in a branching (next slide)
✔ MPST as a wrapper
❖ Offers global protocol on top of HTTP, DNS, …
❖ Example: OAuth, DNS server, SMTP client, …
❖ Utilise concurrent/distributed libraries: Lwt (lightweight threads) and Unix IPC
✔ More Extensions
❖ Scatter/Gather (broadcast)
❖ Undirected choice (-nolive)
❖ Unbalanced Loops
116
B⊕m1 C⊕m2
B⊕m1C⊕m2
A → B
B → C
A → B
117. Full merging
117
let oauth1 =
choice_at s (to_c login_or_cancel)
(s, (s --> c) login @@ (c --> a) password @@ (a --> c) auth finish)
(s, (s --> c) cancel @@ (c --> a) quit finish)
Input labels are
polymorphic variantsA can have different
input branches
Example: A branch happens between S and C.
⇒ Merge A's two behaviours into one
Merging strategy Synopsis Implementations
Plain merging
All branches must have same
labels/continuation (restrictive)
Scalas et al.
(ECOOP 2017)
✔ Full merging
Different input labels/continuations
can be mergeable (more relaxed)
This work
118. • Benchmarks reflect our design decision to use
mutable long lived objects (e.g channel vectors)
❖ Our work: few mutable long lived objects, created at the start of the protocol
❖ State-of-the-art (linear-decomposition [Scala et.a]): many small channels, created on-demand
(during the protocol execution)
118
119. Benchmarks
• Comparison with Linear (Binary) Decomposition [Scalas et al., 2017]
❖ Translation from MPST to Linear Types
❖ Continuation Passing Style (CPS):
It creates (and exchanges) a fresh channel at each communication step
❖ Must be slower than us??
• Compete with ours in some cases (!), but CPS is slower in general
❖ OCaml's major GC is doing nice work
• Monad (static linearity checking with overhead) VS. Direct style (dynamic)
119
120. Put All Slides in right contexts!!
• Most surprising parts:
• (what is it) 簡単に?
programmers can write like this… select opponent role by method, then (a) send by method / (b) receive it and pattern match on variants.
Errors are detected like this… GC well-formedness errors and Local type conformance errors.
Bonus: recursive sessions, session subtyping and delegations are all free.
• (why/how gc-like typing is possible??)
(1) (why EPP is possible, it is operation on types!!: see onion and --> typing.)
(2) (why well-formedness checking is possible? What is well-formedness?: see typing rules)
• (key idea) タマネギ.2枚くらいにおさめる
• (how it is typed) --> typing and implementation (mention that OCaml do not have such first-class?)
• (how deadlock is detected: harder) choice, nondeterminisim and deadlock
choice is like an if-then-else. Two branches are merged into one.
(Sub)typing and global combinator typing prevents occasional wrong branching.
• (how it is imlpemented) lenses and first class record fields
• Choice_at typing and record extensions
❖ Least upper bound is merging
❖ We have more expressive GTs
❖ Subtyping and equi-recursive types
❖ Unguardedness and Unbalanced merging
• Linearity monad
• Deadlock freeness, syntax of local types, and undirected choice (sending and receiving)
• Benchmarks
• Meaning of type errors and relationship to deadlocks
❖ Mergeability and local type syntax
❖ Possibility to extension (i.e. non-directed merging)
❖ Why roles are record fields – what if it was variant tags
120
121. Channel Vectors: Inhabiting MPSTs as simple channel types
121
TA = B⊕{ok.•; cancel.B&cancel.•} TB = A&{ok.•; cancel.B⊕cancel.•}
122. Channel Vectors: Inhabiting MPSTs as simple channel types
122
sok scancel1
scancel2
sok scancel1
scancel2
Idea: Put simply-typed channels in a tree-like data structure
TA = B⊕{ok.•; cancel.B&cancel.•} TB = A&{ok.•; cancel.B⊕cancel.•}
123. Internal Choice is Record
A record having pairs of an output channel and "the rest" in its fields
123
⟦ B⊕{ok.•; cancel.B&cancel(int).•} ⟧s = <B = <ok= (sok, •); cancel= (scancel1, ⟦...⟧s)> >
Pictorially:
124. Internal Choice is Record
A record having pairs of an output channel and "the rest" in its fields
124
⟦ B⊕{ok.•; cancel.B&cancel(int).•} ⟧s = <B = <ok= (sok, •); cancel= (scancel1, ⟦...⟧s)> >
(scancel, )(sok, )
…
ok cancel
Pictorially:
125. Internal Choice is Record
A record having pairs of an output channel and "the rest" in its fields
125
⟦ B⊕{ok.•; cancel.B&cancel(int).•} ⟧s =
: ⟨B : ⟨ok: !(int*int)ו; cancel: !(bool) ×⟦…⟧⟩ ⟩
Provided that
• sok : Och, scancel : Och
(! is an output channel type)
<B = <ok= (sok, •); cancel= (scancel1, ⟦...⟧s)> >
(scancel, )(sok, )
…
ok cancel
Pictorially:
126. External choice is variant input
126
Variant input type: ?[ok_(boolו); cancel_(int× …)]
Variant type: [ok_(boolו); cancel_(int× …)]
("Either ok (with boolו) or cancel (with intׅ)")
("Receives either ok (with boolו) or cancel (with intׅ)")
cancel(_, )ok(_, )
scancel1sok
cancel
external choice
Pictorially: But how?
127. A Solution from Concurrent ML (Reppy, 1993)
127
… has a set of (event) primitives we need!
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
sok : ?bool, scancel : ?int
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
128. A Solution from Concurrent ML (Reppy, 1993)
128
… has a set of (event) primitives we need!
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
129. A Solution from Concurrent ML (Reppy, 1993)
129
… has a set of (event) primitives we need!
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel)
: List[ ?[ok_(boolו), cancel_(intׅ)] ]
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
130. A Solution from Concurrent ML (Reppy, 1993)
130
… has a set of (event) primitives we need!
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel)
: List[ ?[ok_(boolו), cancel_(intׅ)] ]
choose (List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel))
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
131. A Solution from Concurrent ML (Reppy, 1993)
131
… has a set of (event) primitives we need!
choose : List[?T] -> ?T
makes a (multiplexed) channel (external choice)
wrap : (α -> β) -> ?α -> ?β
makes a wrapped channel
sok : ?bool, scancel : ?int
wrap (λx.[ok=(x,•)]) sok
: ?[ok_(boolו)]
wrap (λx.[cancel=(x,…)]) scancel
: ?[cancel_(int×…)]
List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel)
: List[ ?[ok_(boolו), cancel_(intׅ)] ]
choose (List(wrap (λx.[ok=(x,•)]) sok; wrap (λx.[cancel=(x,…)]) scancel))
cancel(_, )ok(_, )
scancel1
sok
(scancel2, )
cancel
choose
: ?[ok_(boolו); cancel_(int× …)] (← wanted)
132. Putting them altogether
132
(scancel, )(sok, )
ok cancel
cancel(_, )ok(_, )
scancel1
sok
(scancel2, )
cancel
choose
B⊕{ok(bool).•; cancel(int).B&cancel(int).•} A&{ok(bool).•; cancel(int).A⊕cancel(int).•}
choice_at A {(A → B) ok finish, (A → B) cancel((B → A) cancel finish)}
cancel(_, )
scancel2
133. Putting them altogether
133
(scancel, )(sok, )
ok cancel
cancel(_, )ok(_, )
scancel1
sok
(scancel2, )
cancel
choose
B⊕{ok(bool).•; cancel(int).B&cancel(int).•} A&{ok(bool).•; cancel(int).A⊕cancel(int).•}
choice_at A {(A → B) ok finish, (A → B) cancel((B → A) cancel finish)}
cancel(_, )
scancel2