How to restrict actor messages to specific types?

匆匆过客 提交于 2019-11-27 12:19:31
Viktor Klang

Then you'd have to encode the message type into the Actor ref, which would drastically decrease the value of something like the ActorRegistry.

Also, with powerful mechanics like "become" (which is fundamental to the actor model) typing the messages is less valuable.

Since Akka doesn't leak memory when a message is not matched to the current behavior, there is not the same risk of sending the "wrong" messages to the "wrong" actor.

Also, Actors are by nature dynamic, so if you want to make them static, use TypedActor (which is not RPC, it's just as RPC as regular actors, void methods are ! calls, Future return type is !!! and other return types are based on !!)

The common practice is to declare what messages an Actor can receive in the companion object of the Actor, which makes it very much easier to know what it can receive.

Does that help?

In Scala stdlib there was an excuse for making basic actors untyped (which is not applicable to Akka, because it doesn't support nested receives, as I remember). Lift, in its turn, supports typed actors out-of-the-box.

However, using channels, it's still possible to create strongly typed actors with stdlib:

object TypedActor {

  def apply[A](fun: PartialFunction[A, Any]): OutputChannel[A] = {
    val sink = new SyncVar[Channel[A]]
    actor {
      val in = new Channel[A](self)
      sink set in
      loop {
        in react { case any => reply(fun(any)) }
      }
    }
    sink.get
  }

}

sealed abstract class FooMessage
case object Foo extends FooMessage
case object Bar extends FooMessage

object Test {

  val fooActor = TypedActor[FooMessage]{
    case Foo => println("OK")
  }

  fooActor ! Foo 
  fooActor ! "Hello!" // doesn't compile -> Type mismatch; found: String("Hello!"); required: FooMessage;

}

Actually restricting an Actor to have only single type as input is not very useful. What is more useful to my mind is to list possible inputs in a strictly typed manner.

There is an approach for strictly typed inputs of actors (SynapseGrid):

case class Contact[T](...)
case class Signal[T](contact:Contact[T], data:T)

In your case the interface consists of a single input contact:

val FooInput = contact[FooMessage]("FooInput")

Within SynapseGrid framework handling of signals is defined with Builder:

class FooActorBuilder extends SystemBuilder {
  inputs(FooInput, OtherInput)
  FooInput.foreach(fooMessage => () //OK
  )
  OtherInput.foreach(...)
}

Obviously one cannot construct Signal with incompatible types. Thus we have compile time checking. In SynapseGrid there is a DSL for working with signals and contacts. For instance, to send Foo or Bar from outside:

val SomeOtherContact = contact[Boolean]("SomeOtherContact")
SomeOtherContact.map(flag => if(flag) Foo else Bar) >> FooInput

Of course one may simply send the message:

val inputMessage = Signal(FooInput, Foo)
actor ! inputMessage
Josh Rosen

It sounds like Akka's Akka's Typed Channel support was to have addressed this, but (according to the comment has already been removed from Akka in version 2.3).

In the documentation for Akka 2.2.3, there a good "design background" section that discusses the difficulties in support typed message sends and responses.

There's also a good NEScala talk by Roland Kuhn, Akka Typed Channels: Implementing Type Calculations as Macros ([YouTube] / [Slides]), that discusses the implementation of typed channels.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!