akka typed — between session types and the actor model
TRANSCRIPT
The Actor Model
• Hewitt, Bishop, Steiger,“A Universal Modular ACTOR Formalism for Artificial Intelligence”, IJCAI’73, pp 235–245
• a model of distributed independent agents
• in response to an incoming message an Actor can • create a finite number of Actors
• send a finite number of messages to Actors it knows
• designate the behavior to be applied to the next message
3
Concrete Implementation: Akka
4
case class Greet(whom: String)
class Greeter extends Actor { def receive = { case Greet(whom) => sender() ! s"Hello $whom!" val delegate = context.actorOf(grumpyProps) context.become(grumpy(delegate)) } def grumpy(delegate: ActorRef): Receive = { case g: Greet => delegate forward g }}
val grumpyProps = Props(new Actor { def receive = { case Greet(whom) => sender() ! s"Go away, $whom!" }})
Initial Motivation
• make Actors re-factoring friendly
• avoid stupid mistakes(i.e. provide some weak safety properties)
6
The Flaws of Previous Implementations
• retaining sender() is just not feasible • this feature is inherently dynamic and therefore must go
• defining union types for multiple input types proved too complex • TypedChannels used a type map based on HList
• verbose syntax and cryptic error messages
• use only single type parameter and subsume other types via dedicated adapters (child Actors)
8
The Current Implementation
• parameterized ActorRef accepts type T
• parameterized Behavior responds to type T
• actor creation turns Behavior[T] via Props[T] into ActorRef[-T]
• implemented as a thin layer on top of untyped actors
• separated the logic from its execution, no more Actor trait
9
Behavior is King, no more Actor trait
10
object Server { sealed trait Command case class Get(id: Int)(val replyTo: ActorRef[Got]) extends Command case class Put(name: String, ref: ActorRef[OtherCommand]) extends Command
case class Got(id: Int, contents: Map[String, ActorRef[OtherCommand]])
val initial: Behavior[Command] = withMap(Map.empty)
private def withMap(map: Map[String, ActorRef[OtherCommand]]) = Total[Command] { case g @ Get(id) => g.replyTo ! Got(id, Map.empty) Same case Put(name, ref) => withMap(map.updated(name, ref)) }}
Encoding Types with Members
12
class MyClass {
def myMethod(id: Int): String def otherMethod(name: String): Unit protected def helper(arg: Double): Unit
}
Encoding Types with Members
• Typed Actors provide complete modules with members
• Typed Actors can encode more flexible access privileges
• more verbose due to syntax being optimized for classes
13
object MyClass { sealed trait AllCommand sealed trait Command extends AllCommand case class MyMethod(id: Int)(replyTo: ActorRef[String]) extends Command case class OtherMethod(name: String) extends Command case class Helper(arg: Double) extends AllCommand
val behavior: Behavior[Command] = behavior(42).narrow private def behavior(x: Int): Behavior[AllCommand] = ???}
Calling Methods
14
object MyClassDemo { import MyClass._ val myClass: MyClass = ??? val myActor: ActorRef[Command] = ??? implicit val t: Timeout = ???
myClass.otherMethod("John") myActor!OtherMethod("John")
val result = myClass.myMethod(42) val future = myActor?MyMethod(42)}
But Actors can do more: Protocols
15
object Protocol { case class GetSession(replyTo: ActorRef[GetSessionResult])
sealed trait GetSessionResult case class ActiveSession(service: ActorRef[SessionCommand]) extends GetSessionResult with AuthenticateResult case class NewSession(auth: ActorRef[Authenticate]) extends GetSessionResult
case class Authenticate(username: String, password: String, replyTo: ActorRef[AuthenticateResult])
sealed trait AuthenticateResult case object FailedSession extends AuthenticateResult
trait SessionCommand}
Attempt at a Definition
• Session: a unit of conversation
• Session Type: the structure of a conversation,a sequence of interactions in a communication-centric program model
• originally only binary sessions, multiparty sessions introduced 2008
• primitives aresending, receiving, sequence, choice, recursion
18
Scribble
• commonly used language for defining protocols • defines the global protocol for all participants
• local projection for a single participant preserves safety
• automatic generation of FSA for local runtime validation
• type discipline for local processes requires support for linear types from the host language
• where that is unavailable use dynamic validation
19
Question
• Can safety be retained while allowing sessions with dynamically added and removed participants?
20
Case Study: Type-Safe Receptionist
22
object Receptionist { trait AbstractServiceKey { type Type } trait ServiceKey[T] extends AbstractServiceKey { final override type Type = T }
sealed trait Command final case class Register[T](key: ServiceKey[T], address: ActorRef[T]) (val replyTo: ActorRef[Registered[T]]) extends Command final case class Find[T](key: ServiceKey[T])(val replyTo: ActorRef[Listing[T]]) extends Command
final case class Registered[T](key: ServiceKey[T], address: ActorRef[T]) final case class Listing[T](key: ServiceKey[T], addresses: Set[ActorRef[T]])
...}
23
object Receptionist { ... val behavior: Behavior[Command] = behavior(TypedMultiMap.empty)
private type KV[K <: AbstractServiceKey] = ActorRef[K#Type]
private def behavior(map: TypedMultiMap[AbstractServiceKey, KV]) = Full[Command] { case Msg(ctx, r: Register[t]) => ctx.watch(r.address) r.replyTo ! Registered(r.key, r.address) behavior(map.inserted(r.key)(r.address)) case Msg(ctx, f: Find[t]) => val set = map get f.key f.replyTo ! Listing(f.key, set) Same case Sig(ctx, Terminated(ref)) => behavior(map valueRemoved ref) }}
Question
• the interrupt feature of Scribble introduces non-determinism since messages can be arbitrarily delayed
• How much does the theory hinge on guaranteed delivery?
• Does the session just cease to exist when nodes fail?
24
Question
• protocol progress between fixed participants implies the destruction of knowledge
• Can this notion be replaced by requiring the acquisition or deduction of new knowledge?
26
Question
• Instead of relying upon consensus, shouldn’t we maximize the use of causality and causal consistency?
27
Lloyd, Freedman, Kaminsky, Andersen: «Don’t Settle for Eventual: Scalable Causal Consistency for Wide-Area Storage with COPS», SOSP’11, ACM 2011
Dynamic Validation of Actor Behavior
• pure internal formulation using an abstract machine (process algebra)
• interpreter can run local projection of protocol
• rejects sending untimely messages
• rejects receiving from inactive channels
• due to at-most-once delivery FSA can only advance upon reception of external input (proof of progress)
29
Question
• Given the following requirements: • sequencing of actions
• dynamic decision how to continue
• recursion
• composition of actions
• injection of plain values
• Is there another abstraction than Monad to formulate the API?
30