Lightweight Session Programming in Scala Alceste Scalas Nobuko - - PowerPoint PPT Presentation

lightweight session programming in scala
SMART_READER_LITE
LIVE PREVIEW

Lightweight Session Programming in Scala Alceste Scalas Nobuko - - PowerPoint PPT Presentation

t i f a c r t A C o m p * t * l e * n t e e * A s t P i W s E n e O o C l l C D O * o * e c C s u u m E e e E R n * o e t v t y s d d a E * a e u l t a Lightweight Session


slide-1
SLIDE 1

Lightweight Session Programming in Scala

Alceste Scalas Nobuko Yoshida

Theory and Applications of Behavioural Types Dagstuhl, February 1st, 2017

C
  • n
s i s t e n t * C
  • m
p l e t e * W e l l D
  • c
u m e n t e d * E a s y t
  • R
e u s e *

*

E v a l u a t e d

* E C O O P *

A r t i f a c t

* A E C

slide-2
SLIDE 2

Introduction lchannels Formal properties Demo Conclusions

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt Session Id is valid Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Failure()

Alt Alt Invalid credentials CreateSession(User) NewSession(S) Success(S)

Alt Alt Valid credentials Alt Alt Session Id does not exist, or is expired Commandi(Ti) Responsei(T ′

i)

. . . . . . Loop/Alt i ∈ {1, . . . , n} Loop/Alt i ∈ {1, . . . , n} Client-server session

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

the from ced- fron- fron- client which the ron- er, The an authen- a who

Scala, Akka Typed & CPS protocols

(R. Kuhn, “Project G˚ albma: Actors vs Types”, slide 42)

Actors interact via untyped mailboxes. Non-trivial protocols must be checked at run-time

slide-3
SLIDE 3

Introduction lchannels Formal properties Demo Conclusions

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt Session Id is valid Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Failure()

Alt Alt Invalid credentials CreateSession(User) NewSession(S) Success(S)

Alt Alt Valid credentials Alt Alt Session Id does not exist, or is expired Commandi(Ti) Responsei(T ′

i)

. . . . . . Loop/Alt i ∈ {1, . . . , n} Loop/Alt i ∈ {1, . . . , n} Client-server session

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

the from ced- fron- fron- client which the ron- er, The an authen- a who

Scala, Akka Typed & CPS protocols

(R. Kuhn, “Project G˚ albma: Actors vs Types”, slide 42)

Actors interact via untyped mailboxes. Non-trivial protocols must be checked at run-time Akka Typed proposes:

  • 1. typed references ActorRef[A]
  • 2. Continuation-Passing Style protocols
slide-4
SLIDE 4

Introduction lchannels Formal properties Demo Conclusions

CPS protocols in Akka Typed (cont’d)

(R. Kuhn, “Project G˚ albma: Actors vs Types”, slide 42)

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt

Session Id is valid

Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Alt Alt

Session Id does not exist, or is expired

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

case class GetSession(id: Int, replyTo: ActorRef[GetSessionResult]) sealed abstract class GetSessionResult case class Active(service: ActorRef[Command]) extends GetSessionResult case class New(authc: ActorRef[Authenticate]) extends GetSessionResult // ... case classes for authentication, etc ...

slide-5
SLIDE 5

Introduction lchannels Formal properties Demo Conclusions

CPS protocols in Akka Typed (cont’d)

(R. Kuhn, “Project G˚ albma: Actors vs Types”, slide 42)

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt

Session Id is valid

Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Alt Alt

Session Id does not exist, or is expired

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

case class GetSession(id: Int, replyTo: ActorRef[GetSessionResult]) sealed abstract class GetSessionResult case class Active(service: ActorRef[Command]) extends GetSessionResult case class New(authc: ActorRef[Authenticate]) extends GetSessionResult // ... case classes for authentication, etc ...

To send a message, produce its continuation

(pseudo-code follows) def client(frontend: ActorRef[GetSession]) = { val cont = spawn[GetSessionResult] { case New(a) => doAuthentication(a) case Active(s) => doSessionLoop(s) } frontend ! GetSession(42, cont) }

slide-6
SLIDE 6

Introduction lchannels Formal properties Demo Conclusions

CPS protocols: opportunities and limitations

case class GetSession(id: Int, replyTo: ActorRef[GetSessionResult]) sealed abstract class GetSessionResult case class Active(service: ActorRef[Command]) extends GetSessionResult case class New(authc: ActorRef[Authenticate]) extends GetSessionResult case class Authenticate(username: String, password: String, replyTo: ActorRef[AuthenticateResult]) sealed abstract class AuthenticateResult case class Success(service: ActorRef[Command]) extends AuthenticateResult case class Failure() extends AuthenticateResult sealed abstract class Command // ... case classes for the client-server session loop ...

CPS protocols are Scala types yielding structured interaction in Akka But they are also:

▸ low-level, cumbersome to write (and read) ▸ not related with any high-level protocol formalism ▸ ambiguous about reusability of (typed) actor references

slide-7
SLIDE 7

Introduction lchannels Formal properties Demo Conclusions

Our approach

Desiderata:

▸ find a formal link between CPS protocols and session types ▸ represent sessions in a language without session primitives

▸ lightweight: no language extensions, minimal dependencies

slide-8
SLIDE 8

Introduction lchannels Formal properties Demo Conclusions

Our approach

Desiderata:

▸ find a formal link between CPS protocols and session types ▸ represent sessions in a language without session primitives

▸ lightweight: no language extensions, minimal dependencies

Inspiration (from concurrency theory):

▸ encoding of session types into linear types for π-calculus (Dardha, Giachino & Sangiorgi, PPDP’12)

slide-9
SLIDE 9

Introduction lchannels Formal properties Demo Conclusions

Our approach

Desiderata:

▸ find a formal link between CPS protocols and session types ▸ represent sessions in a language without session primitives

▸ lightweight: no language extensions, minimal dependencies

Inspiration (from concurrency theory):

▸ encoding of session types into linear types for π-calculus (Dardha, Giachino & Sangiorgi, PPDP’12)

Result: Lightweight Session Programming in Scala

slide-10
SLIDE 10

Introduction lchannels Formal properties Demo Conclusions

lchannels: interface

abstract class In[+A] { def receive(implicit d: Duration): A } abstract class Out[-A] { def send(msg: A): Unit }

API reminds standard Promises/Futures

▸ similar runtime linearity checks and error handling

Note input/output co/contra-variance

slide-11
SLIDE 11

Introduction lchannels Formal properties Demo Conclusions

lchannels: interface

abstract class In[+A] { def receive(implicit d: Duration): A def ?[B](f: A => B)(implicit d: Duration): B = { f(receive) } } abstract class Out[-A] { def send(msg: A): Unit def !(msg: A) = send(msg) }

API reminds standard Promises/Futures

▸ similar runtime linearity checks and error handling

Note input/output co/contra-variance

slide-12
SLIDE 12

Introduction lchannels Formal properties Demo Conclusions

lchannels: interface

abstract class In[+A] { def future: Future[A] def receive(implicit d: Duration): A = { Await.result[A](future, d) } def ?[B](f: A => B)(implicit d: Duration): B = { f(receive) } } abstract class Out[-A] { def promise[B <: A]: Promise[B] // Impl. must be constant def send(msg: A): Unit = promise.success(msg) def !(msg: A) = send(msg) }

API reminds standard Promises/Futures

▸ similar runtime linearity checks and error handling

Note input/output co/contra-variance

slide-13
SLIDE 13

Introduction lchannels Formal properties Demo Conclusions

lchannels: interface

abstract class In[+A] { def future: Future[A] def receive(implicit d: Duration): A = { Await.result[A](future, d) } def ?[B](f: A => B)(implicit d: Duration): B = { f(receive) } } abstract class Out[-A] { def promise[B <: A]: Promise[B] // Impl. must be constant def send(msg: A): Unit = promise.success(msg) def !(msg: A) = send(msg) def create[B](): (In[B], Out[B]) // Used to continue a session }

API reminds standard Promises/Futures

▸ similar runtime linearity checks and error handling

Note input/output co/contra-variance

slide-14
SLIDE 14

Introduction lchannels Formal properties Demo Conclusions

Session programming = In[⋅]/Out[⋅] + CPS protocols

How do we instantiate the In[⋅]/Out[⋅] type parameters?

S In[?] or Out[?] S Out[?] or In[?] Server Client Session types Scala types

slide-15
SLIDE 15

Introduction lchannels Formal properties Demo Conclusions

Session programming = In[⋅]/Out[⋅] + CPS protocols

How do we instantiate the In[⋅]/Out[⋅] type parameters?

S In[A] or Out[A] S Out[A] or In[A] Server Client

CPS protocol classes

A1,A2,...,An Session types Scala types

slide-16
SLIDE 16

Introduction lchannels Formal properties Demo Conclusions

Session programming = In[⋅]/Out[⋅] + CPS protocols

How do we instantiate the In[⋅]/Out[⋅] type parameters?

Linear I/O types S ?(U) or !(U) In[A] or Out[A] S !(U) or ?(U) Out[A] or In[A] Server Client U

CPS protocol classes

A1,A2,...,An Session types Scala types

slide-17
SLIDE 17

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

slide-18
SLIDE 18

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

// Top-level internal choice case class Greet(p: String) case class Quit(p: Unit) // Inner external choice case class Hello(p: String) case class Bye(p: String)

slide-19
SLIDE 19

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String) extends Greeting case class Bye(p: String) extends Greeting

slide-20
SLIDE 20

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String)(val cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String)(val cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

slide-21
SLIDE 21

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String)(val cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String)(val cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

⟪S⟫N = Out[Start]

slide-22
SLIDE 22

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (I)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String)(val cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String)(val cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

⟪S⟫N = Out[Start]

def client(c: Out[Start]): Unit = { if (Random.nextBoolean()) { val (c2in, c2out) = c.create[Greeting]() c.send( Greet("Alice", c2out) ) c2in.receive match { case Hello(name, c3out) => client(c3out) case Bye(name) => () } } else { c.send( Quit() ) } }

slide-23
SLIDE 23

Introduction lchannels Formal properties Demo Conclusions

The “create-send-continue” pattern

We can observe that In/Out channel pairs are usually created for continuing a session after sending a message Let us add the ! ! method to Out[⋅]:

abstract class Out[-A] { ... def !![B](h: Out[A] => B): In[B] = { val (cin, cout) = this.create[A]() // Create... this ! h(cout) // ...send... cin // ...continue } def !![B](h: In[A] => B): Out[B] = { val (cin, cout) = this.create[A]() // Create... this ! h(cin) // ...send... cout // ...continue } }

slide-24
SLIDE 24

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (II)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

slide-25
SLIDE 25

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (II)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

“Session Scala” (pseudo-code)

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

slide-26
SLIDE 26

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (II)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String)(val cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String)(val cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

“Session Scala” (pseudo-code)

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

slide-27
SLIDE 27

Introduction lchannels Formal properties Demo Conclusions

Programming with lchannels (II)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String)(val cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String)(val cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

“Session Scala” (pseudo-code)

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

Scala + lchannels

def client(c: Out[Start]): Unit = { if (Random.nextBoolean()) { val c2 = c !! Greet("Alice")_ c2 ? { case m @ Hello(name) => client(m.cont) case Bye(name) => () } } else { c ! Quit() } }

slide-28
SLIDE 28

Introduction lchannels Formal properties Demo Conclusions

Formal properties

Theorem (Preservation of duality). ⟪S⟫N = ⟪S⟫N

(where In[A] = Out[A] and Out[A] = In[A]).

Theorem (Dual session types have the same CPS protocol classes). prot⟪S⟫N = prot⟪S⟫N . Theorem (Scala subtyping implies session subtyping). For all S,N:

▸ if ⟪S⟫N = In[A] and B <∶ In[A],

then ∃S′,N ′ such that B = ⟪S′⟫N ′ and S′ ⩽ S;

▸ if ⟪S⟫N = Out[A] and Out[A]<∶ B,

then ∃S′,N ′ such that B = ⟪S′⟫N ′ and S ⩽ S′.

slide-29
SLIDE 29

Demo

slide-30
SLIDE 30

Introduction lchannels Formal properties Demo Conclusions

Conclusions

We presented a lightweight integration of session types in Scala based on a formal link between CPS protocols and session types We leveraged standard Scala features (from its type system and library) with a thin abstraction layer (lchannels)

▸ low cognitive overhead, integration and maintenance costs ▸ naturally supported by modern IDEs (e.g. Eclipse)

We validated our session-types-based programming approach with case studies (from literature and industry) and benchmarks

slide-31
SLIDE 31

Introduction lchannels Formal properties Demo Conclusions

Ongoing and future work

Automatic generation of CPS protocol classes from session types, using Scala macros

▸ B. Joseph. “Session Metaprogramming in Scala”. MSc Thesis, 2016

Extend to multiparty session types, using Scribble

▸ A. Scalas, O. Dardha, R. Hu, N. Yoshida.

“A Linear Decomposition of Multiparty Sessions”.

https://www.doc.ic.ac.uk/~ascalas/mpst-linear/

slide-32
SLIDE 32

Introduction lchannels Formal properties Demo Conclusions

Ongoing and future work

Automatic generation of CPS protocol classes from session types, using Scala macros

▸ B. Joseph. “Session Metaprogramming in Scala”. MSc Thesis, 2016

Extend to multiparty session types, using Scribble

▸ A. Scalas, O. Dardha, R. Hu, N. Yoshida.

“A Linear Decomposition of Multiparty Sessions”.

https://www.doc.ic.ac.uk/~ascalas/mpst-linear/

Generalise the approach to other frameworks beyond lchannels, and study its properties. Natural candidates: Akka Typed, Reactors.IO Investigate other programming languages. Possible candidate: C# (declaration-site variance and FP features)

slide-33
SLIDE 33

Introduction lchannels Formal properties Demo Conclusions

Try lchannels!

http://alcestes.github.io/lchannels

C

  • n

s i s t e n t * C

  • m

p l e t e * W e l l D

  • c

u m e n t e d * E a s y t

  • R

e u s e *

*

E v a l u a t e d

* E C O O P *

A r t i f a c t

* A E C

slide-34
SLIDE 34

Backup slides Extensions Evaluation Benchmarks

Session types (in pseudo-Scala)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

slide-35
SLIDE 35

Backup slides Extensions Evaluation Benchmarks

Session types (in pseudo-Scala)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

“Session Scala”

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

slide-36
SLIDE 36

Backup slides Extensions Evaluation Benchmarks

Session vs. linear types + CPS (in pseudo-Scala)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

“Session Scala”

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

“Linear CPS Scala”

def client(c: LinOutChannel[?]): Unit = { if (...) { val (c2in, c2out) = createLinChannels[?]() c.send( Greet("Alice", c2out) ) c2in.receive match { case Hello(name, c3out) => client(c3out) case Bye(name) => () } } else { c.send( Quit() ) } }

slide-37
SLIDE 37

Backup slides Extensions Evaluation Benchmarks

Session vs. linear types + CPS (in pseudo-Scala)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

“Session Scala”

def client(c: S_h): Unit = { if (...) { c ! Greet("Alice") c ? { Hello(name) => client(c) Bye(name) => () } } else { c ! Quit() } }

“Linear CPS Scala”

def client(c: LinOutChannel[?]): Unit = { if (...) { val (c2in, c2out) = createLinChannels[?]() c.send( Greet("Alice", c2out) ) c2in.receive match { case Hello(name, c3out) => client(c3out) case Bye(name) => () } } else { c.send( Quit() ) } }

Can we make it similar to “Session Scala”?

▸ define and implement linear in/out channels ▸ instantiate the “?” type parameter ▸ automate continuation channel creation

slide-38
SLIDE 38

Backup slides Extensions Evaluation Benchmarks

Promises and futures

From the standard library: concurrent programming via promises and futures

slide-39
SLIDE 39

Backup slides Extensions Evaluation Benchmarks

Promises and futures

From the standard library: concurrent programming via promises and futures import scala.concurrent.{Promise, Future, Await} val p = Promise[Int] val f = p.future // Type: Future[Int] // In one thread... p success 42 // ...and in another thread... val v = Await.result(f, timeout) // Will be Success(42)

slide-40
SLIDE 40

Backup slides Extensions Evaluation Benchmarks

Promises and futures

From the standard library: concurrent programming via promises and futures import scala.concurrent.{Promise, Future, Await} val p = Promise[Int] val f = p.future // Type: Future[Int] // In one thread... p success 42 // ...and in another thread... val v = Await.result(f, timeout) // Will be Success(42) Key property: allow to send/receive just one (typed) value

slide-41
SLIDE 41

Backup slides Extensions Evaluation Benchmarks

lchannels: non-distributed implementation

class LocalIn[+A](val future: Future[A]) extends In[A] class LocalOut[-A](p: Promise[A]) extends Out[A] {

  • verride def promise[B <: A] = {

p.asInstanceOf[Promise[B]] // Type-safe cast }

  • verride def create[B]() = LocalChannel.factory[B]()

}

  • bject LocalChannel {

def factory[A](): (LocalIn[A], LocalOut[A]) = { val promise = Promise[A]() val future = promise.future (new LocalIn[A](future), new LocalOut[A](promise)) } }

Just a thin abstraction layer on top of a Promise/Future pair

slide-42
SLIDE 42

Backup slides Extensions Evaluation Benchmarks

Session types = In[⋅]/Out[⋅] + CPS protocols (cont’d)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

slide-43
SLIDE 43

Backup slides Extensions Evaluation Benchmarks

Session types = In[⋅]/Out[⋅] + CPS protocols (cont’d)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

Let us give a name to each non-singleton int/ext choice:

N ⎛ ⎜ ⎝ !Greet(String).( ?Hello(String).X & ?Bye(String).end ) ⊕ !Quit(Unit) ⎞ ⎟ ⎠ = Start N( ?Hello(String).X &?Bye(String).end ) = Greeting

slide-44
SLIDE 44

Backup slides Extensions Evaluation Benchmarks

Session types = In[⋅]/Out[⋅] + CPS protocols (cont’d)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

Let us give a name to each non-singleton int/ext choice:

N ⎛ ⎜ ⎝ !Greet(String).( ?Hello(String).X & ?Bye(String).end ) ⊕ !Quit(Unit) ⎞ ⎟ ⎠ = Start N( ?Hello(String).X &?Bye(String).end ) = Greeting

We get the CPS protocol of S prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String, cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String, cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

slide-45
SLIDE 45

Backup slides Extensions Evaluation Benchmarks

Session types = In[⋅]/Out[⋅] + CPS protocols (cont’d)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

Let us give a name to each non-singleton int/ext choice:

N ⎛ ⎜ ⎝ !Greet(String).( ?Hello(String).X & ?Bye(String).end ) ⊕ !Quit(Unit) ⎞ ⎟ ⎠ = Start N( ?Hello(String).X &?Bye(String).end ) = Greeting

We get the CPS protocol of S and its linear channel endpoint type: prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String, cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String, cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

⟪S⟫N = Out[Start]

slide-46
SLIDE 46

Backup slides Extensions Evaluation Benchmarks

Session types = In[⋅]/Out[⋅] + CPS protocols (cont’d)

S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)

Let us give a name to each non-singleton int/ext choice:

N ⎛ ⎜ ⎝ !Greet(String).( ?Hello(String).X & ?Bye(String).end ) ⊕ !Quit(Unit) ⎞ ⎟ ⎠ = Start N( ?Hello(String).X &?Bye(String).end ) = Greeting Intermediate encoding into linear types with variants and records: S = ! ⎛ ⎜ ⎜ ⎝ µX. ⎡ ⎢ ⎢ ⎢ ⎢ ⎢ ⎣ Greet {p ∶ String,c ∶ !([ Hello {p ∶ String,c ∶ !(X)}, GoodNight {p ∶ String,c ∶ ●} ])}, Quit {p ∶ Unit,c ∶ ●} ⎤ ⎥ ⎥ ⎥ ⎥ ⎥ ⎦ ⎞ ⎟ ⎟ ⎠

We get the CPS protocol of S and its linear channel endpoint type: prot⟪S⟫N =

sealed abstract class Start case class Greet(p: String, cont: Out[Greeting]) extends Start case class Quit(p: Unit) extends Start sealed abstract class Greeting case class Hello(p: String, cont: Out[Start]) extends Greeting case class Bye(p: String) extends Greeting

⟪S⟫N = Out[Start]

slide-47
SLIDE 47

Backup slides Extensions Evaluation Benchmarks

lchannels for distributed interaction

A simple local implementation of lchannels allows for thread interaction

val (cin, cout) = LocalChannel.factory[Start]() // cin, cout have type LocalIn[Start], LocalOut[Start] spawnThread( server(cin) ) spawnThread( client(cout) )

slide-48
SLIDE 48

Backup slides Extensions Evaluation Benchmarks

lchannels for distributed interaction

A simple local implementation of lchannels allows for thread interaction

val (cin, cout) = LocalChannel.factory[Start]() // cin, cout have type LocalIn[Start], LocalOut[Start] spawnThread( server(cin) ) spawnThread( client(cout) )

In/Out can also abstract a distributed communication medium We implemented distributed interaction via:

▸ Akka actors (via serializable In/Out instances) ▸ TCP/IP sockets (via (de-)serialization callbacks) ▸ . . .

more to come, e.g. Reactors.IO (Prokopec & Odersky, Onward!’15)

slide-49
SLIDE 49

Backup slides Extensions Evaluation Benchmarks

Examples and case studies

We used session types, CPS protocols and lchannels to implement several examples and case studies, including e.g.: Sleeping barber

barber, using session types

Customer Shop Barber Full()

Alt Alt No seats Seat()

Available() Ready() Customer(Scut) Descr(String) Haircut() Pay(Int) Alt Alt Seat available

This customer) and session c c He able), case, lea

(types: ∼20 LOC)

Chat server with frontend

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt Session Id is valid Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Failure()

Alt Alt Invalid credentials CreateSession(User) NewSession(S) Success(S)

Alt Alt Valid credentials Alt Alt Session Id does not exist, or is expired Commandi(Ti) Responsei(T ′ i) . . . . . . Loop/Alt i ∈ {1, . . . , n} Loop/Alt i ∈ {1, . . . , n} Client-server session

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

the from ced- fron- fron- client which the ron- er, The an authen- a who

(types: ∼100 LOC)

slide-50
SLIDE 50

Backup slides Extensions Evaluation Benchmarks

Examples and case studies

We used session types, CPS protocols and lchannels to implement several examples and case studies, including e.g.: Sleeping barber

barber, using session types

Customer Shop Barber Full()

Alt Alt No seats Seat()

Available() Ready() Customer(Scut) Descr(String) Haircut() Pay(Int) Alt Alt Seat available

This customer) and session c c He able), case, lea

(types: ∼20 LOC)

Chat server with frontend

Client Frontend Auth App server GetSession(Id) GetSession(Id) Success(S) Active(S) Alt Alt Session Id is valid Failure() GetAuthentication() Authentication(A) New(A) Authenticate(Credentials) Failure()

Alt Alt Invalid credentials CreateSession(User) NewSession(S) Success(S)

Alt Alt Valid credentials Alt Alt Session Id does not exist, or is expired Commandi(Ti) Responsei(T ′ i) . . . . . . Loop/Alt i ∈ {1, . . . , n} Loop/Alt i ∈ {1, . . . , n} Client-server session

notoriously main

  • n-
  • mmunic-

ges. resort- ell

  • f

the from ced- fron- fron- client which the ron- er, The an authen- a who

(types: ∼100 LOC)

▸ Most typical protocol errors are statically ruled out ▸ Timeouts and linearity exceptions take care of the rest ▸ Session delegation is supported — even encouraged!

slide-51
SLIDE 51

Backup slides Extensions Evaluation Benchmarks

Actor-based channels

ActorIn and ActorOut extend In/Out, and send/receive messages by automatically spawning Akka Typed actors

▸ they are both serialisable

(unlike LocalIn/LocalOut)

▸ distributed interaction for free!

(on top of Akka Remoting)

slide-52
SLIDE 52

Backup slides Extensions Evaluation Benchmarks

Actor-based channels

ActorIn and ActorOut extend In/Out, and send/receive messages by automatically spawning Akka Typed actors

▸ they are both serialisable

(unlike LocalIn/LocalOut)

▸ distributed interaction for free!

(on top of Akka Remoting)

For example, on one JVM:

val (in, out) = ActorChannel.factory[Start]() // in, out have type ActorIn[Start], ActorOut[Start] server(in)

slide-53
SLIDE 53

Backup slides Extensions Evaluation Benchmarks

Actor-based channels

ActorIn and ActorOut extend In/Out, and send/receive messages by automatically spawning Akka Typed actors

▸ they are both serialisable

(unlike LocalIn/LocalOut)

▸ distributed interaction for free!

(on top of Akka Remoting)

For example, on one JVM:

val (in, out) = ActorChannel.factory[Start]() // in, out have type ActorIn[Start], ActorOut[Start] server(in)

On another JVM, we can proxy out through its Actor Path:

val c = ActorOut[Start]("akka.tcp://sys@host.com/user/start") // c has type ActorOut[Start] client(c)

slide-54
SLIDE 54

Backup slides Extensions Evaluation Benchmarks

Stream-based channels

StreamIn/StreamOut allow interoperability by reading/writing messages from/to byte streams

slide-55
SLIDE 55

Backup slides Extensions Evaluation Benchmarks

Stream-based channels

StreamIn/StreamOut allow interoperability by reading/writing messages from/to byte streams

Suppose that our “greeting protocol” has a textual format (e.g., from an RFC):

Message Text format Greet(”Alice”) GREET Alice Hello(”Alice”) HELLO Alice Bye(”Alice”) BYE Alice Quit() QUIT

slide-56
SLIDE 56

Backup slides Extensions Evaluation Benchmarks

Stream-based channels

StreamIn/StreamOut allow interoperability by reading/writing messages from/to byte streams

Suppose that our “greeting protocol” has a textual format (e.g., from an RFC):

Message Text format Greet(”Alice”) GREET Alice Hello(”Alice”) HELLO Alice Bye(”Alice”) BYE Alice Quit() QUIT

We define a StreamManager to read/write it (on the right)

class HelloStreamManager(in: InputStream, out: OutputStream) extends StreamManager(in, out) { private val outb = new BufferedWriter(new OutputStreamWriter(out))

  • verride def streamer(x: scala.util.Try[Any]) = x match {

case Failure(e) => close() // StreamManager.close() closes in & out case Success(v) => v match { case Greet(name) => outb.write(f"GREET ${n}\n"); outb.flush() case Quit() => outb.write("QUIT\n"); outb.flush(); close() // End } } private val inb = new BufferedReader(new InputStreamReader(in)) private val helloR = """HELLO (.+)""".r // Matches Hello(name) private val byeR = """BYE (.+)""".r // Matches Bye(name)

  • verride def destreamer() = inb.readLine() match {

case helloR(name) => Hello(name)(StreamOut[Greeting](this)) case byeR(name) => close(); Bye(name) // Session end: close streams case e => { close(); throw new Exception(f"Bad message: ’${e}’") } } }

slide-57
SLIDE 57

Backup slides Extensions Evaluation Benchmarks

Stream-based channels

StreamIn/StreamOut allow interoperability by reading/writing messages from/to byte streams

Suppose that our “greeting protocol” has a textual format (e.g., from an RFC):

Message Text format Greet(”Alice”) GREET Alice Hello(”Alice”) HELLO Alice Bye(”Alice”) BYE Alice Quit() QUIT

We define a StreamManager to read/write it (on the right)

class HelloStreamManager(in: InputStream, out: OutputStream) extends StreamManager(in, out) { private val outb = new BufferedWriter(new OutputStreamWriter(out))

  • verride def streamer(x: scala.util.Try[Any]) = x match {

case Failure(e) => close() // StreamManager.close() closes in & out case Success(v) => v match { case Greet(name) => outb.write(f"GREET ${n}\n"); outb.flush() case Quit() => outb.write("QUIT\n"); outb.flush(); close() // End } } private val inb = new BufferedReader(new InputStreamReader(in)) private val helloR = """HELLO (.+)""".r // Matches Hello(name) private val byeR = """BYE (.+)""".r // Matches Bye(name)

  • verride def destreamer() = inb.readLine() match {

case helloR(name) => Hello(name)(StreamOut[Greeting](this)) case byeR(name) => close(); Bye(name) // Session end: close streams case e => { close(); throw new Exception(f"Bad message: ’${e}’") } } }

val conn = new Socket("host.com", 1337) // Hostname and port of greeting server val strm = new HelloStreamManager(conn.getInputStream, conn.getOutputStream) val c = StreamOut[Start](strm) // Output channel endpoint, to host.com:1337 client(c)

slide-58
SLIDE 58

Backup slides Extensions Evaluation Benchmarks

Run-time and compile-time checks

Well-typed output / int. choice Compile-time Exhaustive input / ext. choice Compile-time

slide-59
SLIDE 59

Backup slides Extensions Evaluation Benchmarks

Run-time and compile-time checks

Well-typed output / int. choice Compile-time Exhaustive input / ext. choice Compile-time Double use of linear output endp. Run-time Double use of linear input endp. Run-time

slide-60
SLIDE 60

Backup slides Extensions Evaluation Benchmarks

Run-time and compile-time checks

Well-typed output / int. choice Compile-time Exhaustive input / ext. choice Compile-time Double use of linear output endp. Run-time Double use of linear input endp. Run-time “Forgotten” output Run-time (timeout on input side) “Forgotten” input Unchecked

slide-61
SLIDE 61

Backup slides Extensions Evaluation Benchmarks

Some benchmarks (lower is better)

34500 36000 37500 39000 40500

Ping-pong (2000000 message exchanges)

15200 16000 16800 lchannels (Promise/ Future) Promise/ Future lchannels (queues) Linked Transfer Queues lchannels (actors) 300 600 900 1200 msecs 150000 165000 180000 195000 210000

Ring (1000 threads, 2000 loops)

lchannels (Promise/ Future) Promise/ Future lchannels (queues) Linked Transfer Queues lchannels (actors) 5000 10000 15000 20000 msecs

Intel Core i7-4790 (4 cores, 3.6 GHz), 16 GB of RAM, Ubuntu 14.04, Oracle JDK 8u72, Scala 2.11.8, Akka 2.4.2

slide-62
SLIDE 62

Backup slides Extensions Evaluation Benchmarks

More benchmarks (lower is better)

54000 56000 58000 60000 62000

Chameneos (256 chameneos, 2000000 meetings)

lchannels (Promise/ Future) Promise/ Future lchannels (queues) Linked Transfer Queues lchannels (actors) 2000 4000 6000 8000 msecs 60000 63000 66000 69000

Streaming (16 threads, 3000000 msgs sent/recvd)

17600 18400 19200 20000 lchannels (Promise/ Future) Promise/ Future lchannels (queues) Linked Transfer Queues lchannels (actors) 30 60 90 120 150 msecs

Intel Core i7-4790 (4 cores, 3.6 GHz), 16 GB of RAM, Ubuntu 14.04, Oracle JDK 8u72, Scala 2.11.8, Akka 2.4.2