正确使用可变/不可变列表

时间:2010-08-29 17:38:06

标签: scala functional-programming immutability

目前,我试图理解Scala中的函数式编程,我遇到了一个我自己无法弄清楚的问题。

想象一下以下情况:

您有两个类: Controller Bot Bot 是一个独立的Actor,由 Controller 启动,执行一些昂贵的操作并将结果返回给 Controller 。因此, Controller 的目的很容易描述:实例化 Bot 的多个对象,启动它们并接收结果。

到目前为止,这么好;我可以在不使用任何可变对象的情况下实现所有这些。

但我该怎么做,如果我必须存储 Bot 返回的结果,以后再将其用作另一个 Bot 的输入(稍后意味着我不知道什么时候在编译时!)?

使用可变列表或集合执行此操作相当容易,但我在代码中添加了很多问题(因为我们在这里处理并发)。

遵循FP范例,是否有可能通过安全地使用不可变对象(列表...)来解决这个问题?

顺便说一句,我是FP新手,所以这个问题可能听起来很愚蠢,但我无法弄清楚如何解决这个问题:)

2 个答案:

答案 0 :(得分:7)

演员通常有内部状态,本身就是可变的野兽。请注意,演员不是FP的东西。

您描述的设置似乎依赖于可变控制器,并且很难用默认情况下非严格的语言来解决它。但是,根据你的工作,你可以依靠未来。例如:

case Msg(info) =>
  val v1 = new Bot !! Fn1(info)
  val v2 = new Bot !! Fn2(info)
  val v3 = new Bot !! Fn3(info)
  val v4 = new Bot !! Fn4(v1(), v2(), v3())
  reply(v4())

在这种情况下 - 因为!!返回Future - v1v2v3将并行计算。消息Fn4正在接收所应用的期货作为参数,这意味着它将等到所有值在计算开始之前计算出来。

同样,只有在计算v4后才会发送回复,因为未来也已应用。

实现这些功能的一个非常实用的方法是功能反应式编程,或简称FRP。它与演员不同。

然而,Scala的美妙之处在于,您可以将这些范例结合到更适合您的问题的范围内。

答案 1 :(得分:6)

这就是类似Erlang的演员在Scala中的样子:

case class Actor[State](val s: State)(body: State => Option[State]) { // immutable
  @tailrec
  def loop(s1: State) {
    body(s1) match {
      case Some(s2) => loop(s2)
      case None => ()
    }
  }

  def act = loop(s)
}

def Bot(controller: Actor) = Actor(controller) { 
  s => 
    val res = // do the calculations
    controller ! (this, res)
    None // finish work
} 

val Controller = Actor(Map[Bot, ResultType]()) {s =>
  // start bots, perhaps using results already stored in s
  if ( 
    // time to stop, e.g. all bots already finished 
  )
    None
  else
    receive {
      case (bot, res) => Some(s + (bot -> res)) // a bot has reported result
    }
}

Controller.act