Lightweight Session Programming in Scala
Alceste Scalas Nobuko Yoshida
Theory and Applications of Behavioural Types Dagstuhl, February 1st, 2017
C- n
- m
- c
- R
*
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
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
Alceste Scalas Nobuko Yoshida
Theory and Applications of Behavioural Types Dagstuhl, February 1st, 2017
C*
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
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
ges. resort- ell
the from ced- fron- fron- client which the ron- er, The an authen- a who
(R. Kuhn, “Project G˚ albma: Actors vs Types”, slide 42)
Actors interact via untyped mailboxes. Non-trivial protocols must be checked at run-time
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
ges. resort- ell
the from ced- fron- fron- client which the ron- er, The an authen- a who
(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:
Introduction lchannels Formal properties Demo Conclusions
(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
ges. resort- ell
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 ...
Introduction lchannels Formal properties Demo Conclusions
(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
ges. resort- ell
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) }
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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)
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
How do we instantiate the In[⋅]/Out[⋅] type parameters?
S In[?] or Out[?] S Out[?] or In[?] Server Client Session types Scala types
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)
Introduction lchannels Formal properties Demo Conclusions
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)
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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
Introduction lchannels Formal properties Demo Conclusions
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]
Introduction lchannels Formal properties Demo Conclusions
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() ) } }
Introduction lchannels Formal properties Demo Conclusions
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 !: In[B] = { val (cin, cout) = this.create[A]() // Create... this ! h(cout) // ...send... cin // ...continue } def !: Out[B] = { val (cin, cout) = this.create[A]() // Create... this ! h(cin) // ...send... cout // ...continue } }
Introduction lchannels Formal properties Demo Conclusions
S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)
Introduction lchannels Formal properties Demo Conclusions
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() } }
Introduction lchannels Formal properties Demo Conclusions
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() } }
Introduction lchannels Formal properties Demo Conclusions
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() } }
Introduction lchannels Formal properties Demo Conclusions
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′.
Introduction lchannels Formal properties Demo 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
Introduction lchannels Formal properties Demo Conclusions
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/
Introduction lchannels Formal properties Demo Conclusions
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)
Introduction lchannels Formal properties Demo Conclusions
C
s i s t e n t * C
p l e t e * W e l l D
u m e n t e d * E a s y t
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
Backup slides Extensions Evaluation Benchmarks
S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)
Backup slides Extensions Evaluation Benchmarks
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() } }
Backup slides Extensions Evaluation Benchmarks
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() ) } }
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
From the standard library: concurrent programming via promises and futures
Backup slides Extensions Evaluation Benchmarks
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)
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
class LocalIn[+A](val future: Future[A]) extends In[A] class LocalOut[-A](p: Promise[A]) extends Out[A] {
p.asInstanceOf[Promise[B]] // Type-safe cast }
}
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
Backup slides Extensions Evaluation Benchmarks
S = µX.(!Greet(String).(?Hello(String).X & ?Bye(String).end) ⊕ !Quit.end)
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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]
Backup slides Extensions Evaluation Benchmarks
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]
Backup slides Extensions Evaluation Benchmarks
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) )
Backup slides Extensions Evaluation Benchmarks
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)
Backup slides Extensions Evaluation Benchmarks
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 sessionnotoriously main
ges. resort- ell
the from ced- fron- fron- client which the ron- er, The an authen- a who
(types: ∼100 LOC)
Backup slides Extensions Evaluation Benchmarks
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 sessionnotoriously main
ges. resort- ell
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!
Backup slides Extensions Evaluation Benchmarks
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)
Backup slides Extensions Evaluation Benchmarks
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)
Backup slides Extensions Evaluation Benchmarks
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)
Backup slides Extensions Evaluation Benchmarks
StreamIn/StreamOut allow interoperability by reading/writing messages from/to byte streams
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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))
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)
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}’") } } }
Backup slides Extensions Evaluation Benchmarks
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))
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)
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)
Backup slides Extensions Evaluation Benchmarks
Well-typed output / int. choice Compile-time Exhaustive input / ext. choice Compile-time
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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
Backup slides Extensions Evaluation Benchmarks
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