Akka向远程actor发送闭包。

7

背景

我想要向远程actor发送一个closure。远程actor应该在其数据上运行closure并返回结果。也许这并不可取,但出于好奇,这就是我现在想做的事情

但是我注意到,如果一个closure被创建为匿名函数,它也会捕获外部对象并尝试进行编组,如果外部对象不可序列化,则会失败,就像这种情况。

class Client(server: ActorRef) extends Actor {

  var every = 2

  override def preStart() = {
    println("client started. sending message....")
    server ! new Message((x) => x % every == 0)
  }

}

上面的代码在调用远程actor时会生成异常。我可以在方法preStart()中定义一个本地变量:val every_ = every,并用它替换actor成员变量。但我觉得这是一种变通方法而不是解决方案。如果闭包稍微复杂一些,我将不得不非常小心。
另一种方法是定义一个从Function1[A,B]继承的类,并发送其实例作为闭包。
class MyFunc(every : Int) extends Function1[Int,Boolean] with Serializable {

  def apply(v1 :Int) : Boolean = {
    v1 % every == 0
  }  
}


server ! new Message(new MyFunc(every))

但这将闭包定义与其使用的位置分开,破坏了使用函数式语言的整个目的,并使得定义闭包逻辑变得更加困难。

具体问题

我是否可以推迟定义Function1.apply的主体,并在我从本地定义的闭包创建MyFunc实例时分配apply的主体?

例如:

server ! new Message(new MyFunc(every){ // not valid scala code
  x % every == 0
})

every 是一个本地变量吗?

基本上,我想结合这两种方法,即将 Function1 的一个对象和在创建 Function1 实例的地方定义的匿名函数的函数体一起发送到远程 actor。

谢谢!


我猜你知道你在做什么,但我想确保你知道发送闭包被认为是一种不良实践,正如在文档中清楚解释的“Actor 最佳实践”一章第3段所述。 - Bruno Grieder
谢谢你指出来。或者我可以将该行为封装在一个Actor中,并动态创建它?即根据闭包来指定Actor的行为,而不是将闭包本身发送到Actor。 - weima
你可以做很多事情,但我有一种感觉,你正在尝试实现一个特定问题的“错误”/笨拙解决方案。我相信如果你编辑你的问题并描述你想要实现什么,你会在SO上得到更好的回答。 - Bruno Grieder
好的。我重新表述问题以针对特定查询。如何动态定义Funtion1[A,B]apply()方法的主体?就像在Java中实例化接口时定义匿名类方法一样。 - weima
谢谢,我刚刚检查过了,确实可以在创建时定义Function1的主体。谢谢 :) - weima
1个回答

4
当然,您可以将行为发送给actor,但这被认为是不好的实践,而您的问题是“为什么”的好答案。
正如BGR所指出的那样,文档中有一个专门的部分来回答这个问题,但它没有示例。
因此,当您将闭包作为消息发送时,您会发送一些额外的“隐含”状态。正如文档中所述,它可能不可变,但即使在这种情况下,它也可能会造成问题。
Scala的问题在于它不是严格的函数式语言 - 它是多范式语言。换句话说,您可以同时具有功能范式和命令式风格的代码。例如,纯粹的函数式语言haskell就没有这样的问题。
对于您的“特定查询”,我建议您使用一组预定义的函数。虽然这完全等同于使用闭包的变体,但语法有点啰嗦。由于您不会在运行时生成代码,您使用的所有函数都定义在有限集合中,并且(看起来)由值参数化。这使得您的代码不像使用闭包那样灵活,但最终它们会是等效的情况。
因此,作为我所有帖子的主旋律:如果您要向演员发送行为,则应该是坚如磐石的原子性(即没有任何依赖关系)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接