【问题标题】:Purity, Referential Transparency and State Monad纯度、参照透明度和状态单子
【发布时间】:2015-06-02 10:44:53
【问题描述】:

我目前正在设计一种数值算法,作为其操作的一部分,它需要多次更新doubles 的向量。由于算法必须尽可能节省空间和时间,我不想编写传统类型的 FP 代码,因为这种代码在每次操作后都会在后台创建许多版本的数据结构。我也不想创建可变数据结构并使它们全局可用。因此,我决定使用可变数据结构,然后选择在State monad 中执行可变操作。由于这是我第一次尝试使用 State monad,我想确认我是否有

  1. 保留的引用透明度
  2. 保持功能纯度

update 函数转换数据结构状态。由于破坏性更新是在这个函数内部进行的,并且无法从外部获取数据结构的句柄,所以我认为这个函数是纯粹的并且是引用透明的。

def update(i : Int,d : Double) = State[ArraySeq[Double], Unit]{
  case xs: ArraySeq[Double] => {xs(i) = d; (xs, ())}
}

app 函数是一个玩具函数,它将消耗 doubles 序列并修改其状态:

def app : State[ArraySeq[Double], Unit] = for{
    _ <- update(0, 3.142)
  // do a heap of stuff on ArraySeq
}yield()

呼叫:

app(Vector(0.0, 1.0, 2.0, 3.0, 4.0).to[ArraySeq])._1.to[Vector]

结果:

res0: Vector[Double] = Vector(3.142, 1.0, 2.0, 3.0, 4.0)

【问题讨论】:

  • 你使用的是scalaz的State还是你自己写的?

标签: scala functional-programming


【解决方案1】:

我猜你可以说你的update 本身是纯粹的,因为它只是代表一些突变,但是一旦你运行它,所有的赌注都没有了:

scala> val xs = List(1.0, 2.0, 3.0).to[ArraySeq]
xs: scala.collection.mutable.ArraySeq[Double] = ArraySeq(1.0, 2.0, 3.0)

scala> update(0, 10).eval(xs)
res0: scalaz.Id.Id[Unit] = ()

scala> xs
res1: scala.collection.mutable.ArraySeq[Double] = ArraySeq(10.0, 2.0, 3.0)

这是一个糟糕的场景,它与纯粹或参照透明相反。

State 在你的例子中并没有真正给你买任何东西——事实上你调用app 的方式是你有一个没有其他人可以改变的ArraySeq。您不妨咬紧牙关,在您控制的范围内以通常的方式使用可变数据结构——即,像这样编写app

def app(xs: Vector[Double]): Vector[Double] = {
  val arr = xs.to[ArraySeq]
  // Perform all your updates in the usual way
  arr.toVector
}

这实际上纯粹且引用透明,但它也比State 版本更诚实。如果我看到State[Foo, Unit] 类型的值,我的假设是该值代表某种将Foo 更改为新Foo 的操作,没有改变原始@ 987654332@。这就是状态 monad 的全部内容——它提供了一种很好的方式来对不可变数据结构上的操作进行建模,并以一种看起来有点像突变的方式组合它们。如果您将它与 实际 突变混合使用,您可能会混淆使用您代码的任何人。

如果你真的想要真正的突变和纯洁,你可以看看 Scalaz 的STArray。这是解决这个问题的一个非常聪明的方法,在 Haskell 这样的语言中,这是一种很有意义的方法。不过,我自己的感觉是,它在 Scala 中几乎总是错误的解决方案。如果你真的需要可变数组的性能,只需使用本地可变数组并确保不会将其泄漏到外部世界。如果您不需要那种性能(而且大多数时候不需要),请使用State 之类的东西。

【讨论】:

  • 天哪!关于改变状态单子内部的原始结构的观点;我有一种感觉,我在做一些可疑的事情,但看不到它是什么。我看了一下STArray,它正好解决了我的问题任务。感谢您的洞察力和建议。
猜你喜欢
  • 2011-03-17
  • 2021-06-18
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 2011-06-18
  • 1970-01-01
  • 2015-05-13
相关资源
最近更新 更多