这个问题更具理论性。 我有一个对象,它包含一个私有的可变列表或映射,其增长只是附加。我相信我可以争辩说,对象本身是功能性的,在功能上是透明的,并且对于所有外观都是不可改变的。所以例如我有
import scala.collection.mutable.Map
case class Foo(bar:String)
object FooContainer {
private val foos = Map.empty[String, Foo]
def getFoo(fooName:String) = foos.getOrElseUpdate(fooName, Foo(fooName))
}
我可以用列表做类似的情况。现在我想要真正的功能,我需要确保线程安全,我想我可以简单地使用锁,同步或原子引用。我的问题是这一切都是必要的&足够的,这是一种常见的做法,是否存在这种行为的既定模式?
答案 0 :(得分:2)
我会说“功能性”确实是错误的术语。上面显示的确切示例可以称为“纯”,因为它的输出仅由其输入定义,因此没有任何(可见的)副作用。实际上它是不纯的,因为它隐藏了内部状态。
在给定相同参数值的情况下,函数始终评估相同的结果值。函数结果值不能依赖于程序执行过程中或程序的不同执行之间可能发生变化的任何隐藏信息或状态,也不依赖于来自I / O设备的任何外部输入。 Wikipedia
如果你使Foo
类可变:
case class Foo(bar:String) {
private var mutable:Int = 1
def setFoo(x:Int) = mutable = x
def foo = mutable
}
Foo
类的可变性导致getFoo
不纯:
scala> FooContainer.getFoo("test").foo
res5: Int = 1
scala> FooContainer.getFoo("bla").setFoo(5)
scala> FooContainer.getFoo("bla").foo
res7: Int = 5
为了使函数看起来纯粹,FooContainer.getFoo("bla").foo
必须始终返回相同的值。因此,所描述的构建体的“纯度”充其量是脆弱的。
答案 1 :(得分:1)
作为一般规则,你最好使用var
持有一个不可变的集合类型,它在更新时被替换,而不是你持有一个可变类型的val
。前者从不处理可能在创建后发生变化的值(除了持有不断变化的值本身的类)。然后,您只需要小心谨慎地了解var
保存的更新值何时发布,但由于更新引用通常是原子的(除了存储long
或{{1在32位机器上运行RAM,风险很小。