5

I just finished Martin Odersky's scala class at Coursera. Scala being my first FP language, I was excited by the idea of limiting mutable state. This allows for much easier concurrency and also makes the code super maintainable.

While learning all this, I realized you could guarantee the mutability of an object as long as it had no mutable variables and only referenced immutable objects. So now I can do everything by creating a new state instead of modifying an old one, use tail recursion when possible.

Great. So I can only do this so far up the chain. At some point, my application needs to be able to modify some existing state. I know where put in concurrency control at this point, locks, blah blah. I'm still defaulting to my standard multi-threaded concurrency control I've always used.

Oh scala community, is there a better way? Monads maybe?

EDIT: this question is a bit general, so I wanted to give a use case: I have a machine learning algorithm that stores several collections of data. They have functions that return updated representations of the data (training, etc), all immutable. Ultimately I can keep this return-updated-state pattern going up the chain to the actual object running the simulation. This has a state that is mutable and holds references to the collections. I may want to distributed to multi-cores, or multi-system.

4

3 回答 3

4

这是一个有点主观的问题,所以我不会试图回答其中的“哪个是最好的”部分。如果您主要关心的是多线程并发上下文中的状态,那么一种选择可能是软件事务内存

Akka提供了 STM的实现 (参见快速入门)。根据您的用例,它可能是重量级的或矫枉过正的,但话又说回来,它可能比一堆锁更可取。与锁不同,STM 倾向于乐观,就像数据库事务一样。与数据库事务一样,您在事务上下文中显式更改共享状态,并且您描述的更改将自动提交或在检测到冲突时重新尝试。基本上,您必须将所有状态包装在Refs中,这些状态只能在“原子”块中进行操作 - 实现为采用闭包的方法,您可以在其中使用操作您的Refs 和 ScalaSTM 确保对您的状态的整套操作成功或失败 - 不会有中途或不一致的更改。

这利用了 Scala 的隐式参数- 对 s 的所有操作都Ref需要一个事务对象作为参数,这由给定的闭包接收atomic并且可以声明为隐式,因此 will 中的所有代码atomic都可以以非常自然但安全的风格编写。

要注意的是,要使此功能有用,您确实需要使用提供的事务数据结构;所以这将意味着使用TSet而不是SetTMap而不是Map当在事务上下文(在原子块内)中使用时,它们提供全有或全无的更新语义。这很像clojure 的持久化集合。您还可以Ref使用 s 构建您自己的事务数据结构,以便在这些atomic块中使用。

如果你不反感括号的话,clojure 对 refs 的解释真的很好:http ://clojure.org/refs

于 2012-12-10T16:17:15.300 回答
2

Depending on your use case you might be able to stick with deeply immutable object structures which you partially copy instead of actually mutating them (similar to an "updated" immutable list that shares a suffix with its original list). So-called lenses are a nice way of dealing with such structures, read about them in this SO question or in this blog post.

Sticking with immutable structures of course only works if you don't want changes to be globally observable. An example where immutable structures are most likely not an option are two concurrent clients working on a shared list, where the modifications done by client A must be observable by client B, and vice versa.

于 2012-12-10T15:44:09.020 回答
0

我建议最好的方法是将可变变量存储在 Akka Actor 中,使用传入和传出 Akka Actor 的消息来发送和接收这个可变引用。使用不可变的数据结构。

我有一个 StorageActor 如下。每次通过 StoreEntity 存储某些内容时,都会更新变量 entityMap。此外,它不需要易变并且仍然有效。

Akka actor 是可以改变事物的地方,消息在纯函数世界中传入和传出。

import akka.actor.Actor
import java.util.UUID
import com.orsa.minutesheet.entity.Entity

case class EntityRef(entity: Option[Entity])

case class FindEntity(uuid: UUID)
case class StoreEntity[T >: Entity](uuid: UUID, entity: Option[T])

class StorageActor extends Actor {

  private var entityMap = Map[UUID, Entity]()

  private def findEntityByUUID(uuid:UUID): Option[Entity] = entityMap.get(uuid)

  def receive = {
    case FindEntity(uuid) => sender ! EntityRef( findEntityByUUID(uuid) )
    case StoreEntity(uuid, entity) =>
      entity match {
        case Some(store) => entityMap += uuid -> store.asInstanceOf[Entity]
        case None => entityMap -= uuid
      }
  }
}
于 2013-12-04T15:18:25.073 回答