functional-programming - move 语义对于 Rust 中的引用透明性意味着什么?

标签 functional-programming rust move-semantics referential-transparency

我正在尝试弄清楚 move 语义如何影响引用透明度。

Referential transparency (RT) 允许我们在不改变程序含义的情况下用其结果替换任何表达式(从 Functional Programming in Scala 释义)。例如,我可以替换 1 + 1在我的程序中的任何地方使用 2 ,什么都不应该改变。这个 Python 程序是引用透明的:

@dataclass
class Bucket:
    things: List[str]

leaves = ["leaves"]

def bucket_with_sand(things: List[str]) -> Bucket:
    return Bucket(things + ["sand"])

bucket_with_sand(leaves)  # can be replaced with Bucket(["leaves", "sand"]) with no change to the program

而这个函数就地改变了它的参数

def bucket_with_sand(things: List[str]) -> Bucket:
    things += ["sand"]
    return Bucket(things)

因此用其结果替换函数调用会改变含义。它不再具有引用透明性。用 move semantics like Rust's 的语言,我们可以通过 move leaves 来避免这个问题。 (并依赖于 Vec 不是 Copy 的事实):

struct Bucket {
    things: Vec<&str>,
}

let leaves = vec!["leaves"];

fn bucket_with_sand(things: Vec<&str>) -> Bucket {
    things.push("sand");
    Bucket { things }
}

bucket_with_sand(leaves); // mutates `things`
// doesn't matter that `leaves` has been mutated here as it's now out of scope

这似乎再次具有引用透明性。这样对吗?这些举措是否放松了对 RT 设计的传统限制?或者 Action 不是参照透明的?我特别想知道是否对 RT 有更广泛的影响,我还没有看到。

最佳答案

在几乎所有在真实计算机上执行的语言中,引用透明的概念都有些模糊,尤其是在具有命令式状态的语言中,Rust 也不异常(exception)。调用可能会产生副作用——从执行 IO 到内存不足,再到仅仅改变一个可变变量——根据你是否包括那些你认为“没有变化”的函数,你可能会认为函数是非-参照透明。它们不是纯数学函数,而是在调用时确实会改变世界状态的过程。

也就是说:Rust 所谓的“所有权”系统——它的“仿射”或“move ”类型与其多读者/单作者借用系统的组合——用于显着减少可能的副作用一个程序。特别是它(主要是*)消除了大多数其他命令式语言中最普遍和最有害的副作用:可变别名 .也就是说,在 Rust 中,您(大多数情况下*)永远不会有两个或多个对同一内存位置的引用,其中一个函数中的一个引用会改变内存位置作为运行的副作用,而另一个函数中的另一个引用只会看到内存位置中的值“突然改变”。这意味着任何时候一个值要被改变,它都会通过它唯一的当前引用来改变——要么是 &mut或者一个拥有变量——这意味着,正如你在这里问的那样,在某种程度上,关于引用透明度的假设在 Rust 中比在大多数其他命令式语言中更有可能成为真。

上面的“(moSTLy*)”星号说明了另一个相对较大的异常:不安全的代码可能违反此规则,并且在几个库函数中也是如此。例如 portion of Rust's standard library提供所谓的“内部可变性”提供了一个 unsafe cell type以及以时间方式动态强制禁止可变别名的包装器类型:这样的可变访问可以在给定时间发生,但允许它们按顺序从不同的共享引用顺序发生。

同样的警告适用于几乎所有真正的语言,无论它推销自己多么“纯粹”:ML 系列有 ref单元格,Haskell 有它的 unsafe library functions , Lisps 有 set!等等。这些都是对这样一个事实的让步:有时能够通过数学抽象(函数语言中的纯值,或 Rust 中的仿射值)到达具有不受限制的可变别名的底层机器,具有压倒性的性能优势。

关于functional-programming - move 语义对于 Rust 中的引用透明性意味着什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59696588/

相关文章:

Swift:避免命令式 For 循环

c++ - 进一步的右值引用和临时对象

rust - Rust 错误 : cannot call a method whose type contains a self-type through an object

rust - 如何解决这个与生命周期相关的错误?

c++ - 存在完美的转发构造函数时,按左值引用转发构造函数的目的是什么?

c++11 - std::move-如何警告程序员不要使用* move 自*对象

generics - 如何在Typescript中参数化函数参数元组

scala - 为什么 Shapeless 中的 _0 Nat 是一个类而不是一个对象?

c++ - 就常量性和引用性而言,函数应该如何接受 *lambda* 参数?

image - 复制/move 未实现复制的字段