如何将参与者消息限制为特定类型?

发布于 2024-10-30 21:08:18 字数 670 浏览 6 评论 0原文

Akka 中,除了使用使用RPC 风格的编程模型?

我可以在 Akka 中使用消息传递风格而不放弃 Actor 边界的静态类型安全吗?

例如,我想使用这样的代码:

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

class FooActor extends Actor[FooMessage] {
  def receive = {
    case Foo => () // OK

    // Would raise a compiler error:
    // case s: String => error("Can't happen, String is not a subtype of FooMessage") 

  }
}

val fooActor = actorOf[FooActor]
fooActor ! Foo // OK

// Won't compile:
fooActor ! "Hello"

也许必须扩展一些基本特征或具有像 Either 这样的构造才能允许系统级消息(Exit , ETC。)。

In Akka, is there a way to restrict messages to actors to be of a specific static type other than using the "Typed Actor" APIs that use an RPC style programming model?

Can I use the message passing style with Akka without throwing away static type safety at the actor boundaries?

For example, I'd like to use code like this:

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

class FooActor extends Actor[FooMessage] {
  def receive = {
    case Foo => () // OK

    // Would raise a compiler error:
    // case s: String => error("Can't happen, String is not a subtype of FooMessage") 

  }
}

val fooActor = actorOf[FooActor]
fooActor ! Foo // OK

// Won't compile:
fooActor ! "Hello"

Perhaps one would have to extend some base trait or have a construct like Either to allow for system level messages (Exit, etc.).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

橘寄 2024-11-06 21:08:18

然后,您必须将消息类型编码到 Actor 引用中,这将大大减少 ActorRegistry 等内容的值。

此外,使用“成为”(这是演员模型的基础)等强大的机制,输入消息的价值就降低了。

由于当消息与当前行为不匹配时,Akka 不会泄漏内存,因此不存在将“错误”消息发送给“错误”参与者的相同风险。

另外,Actor 本质上是动态的,所以如果你想让它们静态,请使用 TypedActor (这不是 RPC,它就像常规 Actor 一样的 RPC,void 方法是 ! 调用,Future 返回类型是 !!! 以及其他返回类型是基于!!)

常见的做法是在 Actor 的伴生对象中声明 Actor 可以接收哪些消息,这使得更容易知道它可以接收什么。

这有帮助吗?

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?

我爱人 2024-11-06 21:08:18

在 Scala stdlib 中,有一个借口让基本的 actor 成为无类型的(这不适用于 Akka,因为我记得它不支持嵌套接收)。 Lift 则支持开箱即用的类型演员。

然而,使用通道,仍然可以使用 stdlib 创建强类型的 actor:

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;

}

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;

}
油焖大侠 2024-11-06 21:08:18

实际上限制 Actor 仅具有单一类型作为输入并不是很有用。对我来说更有用的是以严格类型化的方式列出可能的输入。

有一种用于严格类型化参与者输入的方法(SynapseGrid):

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

在您的情况下,界面由单输入触点:

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

在 SynapseGrid 框架内,信号处理是使用 Builder 定义的:

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

显然,无法构造具有不兼容类型的 Signal。因此我们进行了编译时检查。在 SynapseGrid 中,有一个用于处理信号和联系人的 DSL。例如,从外部发送 Foo 或 Bar:

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

当然可以简单地发送消息:

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

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
别再吹冷风 2024-11-06 21:08:18

听起来像 Akka 的 Akka 的类型化通道支持是要解决这个问题,但是(根据 评论 已经在 2.3 版本中已从 Akka 中删除)。

在 Akka 2.2.3 的文档中,有一个很好的 “设计背景”部分讨论了支持类型化消息发送和响应的困难。

Roland Kuhn 也发表了一篇精彩的 NEScala 演讲,Akka Typed Channels: Implementing Type Calculations as Macros ([YouTube] / [幻灯片]),讨论类型化通道的实现。

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.

醉南桥 2024-11-06 21:08:18

Akka 现在已完全类型化,根据文档。无类型的 Actor 被称为“Akka Classic”...

例如,对于仅接受 Greet 消息的 Actor (ref),您拥有

ActorRef<Greet> myactor;

(在撰写本文时当前版本为 2.9.1。
不精通 Java 或 Scala,抱歉。)

Akka is now fully typed, according to the docs. And untyped actors are called "Akka Classic"...

For example, for an actor (ref) accepting only Greet messages, you have

ActorRef<Greet> myactor;

(Current version is 2.9.1 at the time of writing.
Not proficient in Java or Scala, sorry.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文