3. 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
4. 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!"
}
})
8. 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
9. 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
10. 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))
}
}
12. Encoding Types with Members
12
class MyClass {
def myMethod(id: Int): String
def otherMethod(name: String): Unit
protected def helper(arg: Double): Unit
}
13. 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] = ???
}
14. 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)
}
15. 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
}
18. Attempt at a Definition
• Session: aunitofconversation
• Session Type: thestructureofaconversation,
a sequence of interactions in a communication-
centric program model
• originally only binary sessions, multiparty sessions
introduced 2008
• primitives are
sending,receiving,sequence,choice,recursion
18
19. 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
20. Question
• Can safety be retained while allowing sessions with
dynamically added and removed participants?
20
22. 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. 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)
}
}
24. 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
26. 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
27. 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
29. 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
30. 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