我可以以纯函数的方式改变变量吗?
Can I mutate a variable in place in a purely functional way?
我知道我可以使用状态传递和状态 monad 来实现纯功能性突变,但据我所知,这还不合适,我希望就地实现它的性能优势。
举个例子就好了,例如给数字加 1,最好是在 Idris 中,但 Scala 也不错
p.s。有突变标签吗?一个都看不到
不,这在 Scala 中是不可能的。
然而,在纯函数式语言中实现就地突变的性能优势是可能的。例如,让我们使用一个以纯函数方式更新数组的函数:
def update(arr: Array[Int], idx: Int, value: Int): Array[Int] =
arr.take(idx) ++ Array(value) ++ arr.drop(idx + 1)
为了保持纯度,这里需要复制数组。原因是如果我们就地改变它,我们将能够在调用函数后观察到:
def update(arr: Array[Int], idx: Int, value: Int): Array[Int] = {
arr(idx) = value
arr
}
以下代码在第一个实现中可以正常工作,但在第二个实现中会中断:
val arr = Array(1, 2, 3)
assert(arr(1) == 2)
val arr2 = update(arr, 1, 42)
assert(arr2(1) == 42) // so far, so good…
assert(arr(1) == 2) // oh noes!
纯函数式语言的解决方案是简单地禁止最后一个断言。如果您无法观察到原始数组发生变异的事实,那么就地更新数组也没有错!实现这一点的方法称为线性类型。线性值是您可以 恰好一次 使用的值。将线性值传递给函数后,编译器将不允许您再次使用它,从而解决了问题。
我知道有两种语言具有此功能:ATS 和 Haskell。如果您想了解更多详细信息,我推荐 Simon Peyton-Jones 的演讲,他在 Haskell:
中解释了实现
对线性类型的支持已合并到 GHC 中:https://www.tweag.io/blog/2020-06-19-linear-types-merged/
我知道我可以使用状态传递和状态 monad 来实现纯功能性突变,但据我所知,这还不合适,我希望就地实现它的性能优势。
举个例子就好了,例如给数字加 1,最好是在 Idris 中,但 Scala 也不错
p.s。有突变标签吗?一个都看不到
不,这在 Scala 中是不可能的。
然而,在纯函数式语言中实现就地突变的性能优势是可能的。例如,让我们使用一个以纯函数方式更新数组的函数:
def update(arr: Array[Int], idx: Int, value: Int): Array[Int] =
arr.take(idx) ++ Array(value) ++ arr.drop(idx + 1)
为了保持纯度,这里需要复制数组。原因是如果我们就地改变它,我们将能够在调用函数后观察到:
def update(arr: Array[Int], idx: Int, value: Int): Array[Int] = {
arr(idx) = value
arr
}
以下代码在第一个实现中可以正常工作,但在第二个实现中会中断:
val arr = Array(1, 2, 3)
assert(arr(1) == 2)
val arr2 = update(arr, 1, 42)
assert(arr2(1) == 42) // so far, so good…
assert(arr(1) == 2) // oh noes!
纯函数式语言的解决方案是简单地禁止最后一个断言。如果您无法观察到原始数组发生变异的事实,那么就地更新数组也没有错!实现这一点的方法称为线性类型。线性值是您可以 恰好一次 使用的值。将线性值传递给函数后,编译器将不允许您再次使用它,从而解决了问题。
我知道有两种语言具有此功能:ATS 和 Haskell。如果您想了解更多详细信息,我推荐 Simon Peyton-Jones 的演讲,他在 Haskell:
中解释了实现对线性类型的支持已合并到 GHC 中:https://www.tweag.io/blog/2020-06-19-linear-types-merged/