为什么纯函数式语言只允许按值传递参数?

Why purely functional languages allow passing parameters only by value?

我是函数式语言的新手,我想知道为什么我们不能通过引用传递参数。 我发现 anserws 说

you are not supposed to change the state of objects once they have been created

但我不太明白。

因为如果您通过引用传递参数,您可能会更改参数中的某些内容,这可能会带来副作用。考虑一下:

function pay(person, cost) {
  person.wallet -= cost;
}

function money(person) {
  return person.wallet;
}

let joe = { name: "Joe", wallet: 300 };
console.log(money(joe));   // 300
pay(joe, 20);
console.log(money(joe));   // 280

两个money(joe)接受相同的输入(对象joe)并给出不同的输出(300、280)。这与纯函数式语言的定义相矛盾(所有函数在给定相同输入时必须 return 相同输出)。

如果程序是这样制作的,没有问题:

    function pay(person, cost) {
      return Object.freeze({ ...person, wallet: person.wallet - cost });
    }

    function money(person) {
      return person.wallet;
    }

    let joe = Object.freeze({ name: "Joe", wallet: 300 });
    console.log(money(joe));                 // 300
    let joe_with_less_money = pay(joe, 20);
    console.log(money(joe));                 // still 300
    console.log(money(joe_with_less_money)); // 280

这里我们必须通过冻结对象(这使它们不可变)来伪造按值传递,因为 JavaScript 只能以一种方式传递参数(通过共享传递),但想法是相同的。

(这预设了适用于 C++ 等语言的术语 "pass-by-reference" 的含义,其中实现细节影响可变性,而不是现代语言的实际实现细节,其中引用通常在幕后传递但通过其他方式确保不变性。)

我认为你误解了这个概念。 Scheme 和 C/C++ 都是按值传递的语言,大多数值都是地址(引用)。

纯函数式语言可以有引用,并且那些是按值传递的。他们没有的是在同一范围内重新定义变量(变异绑定),并且他们没有可能更新引用指向的对象。所有操作return一个全新的对象。

作为示例,我可以为您提供 Java 的字符串。 Java 不是纯粹的功能,但它的字符串是。如果您将字符串更改为大写,您会在 return 中得到一个新的字符串对象,并且原始对象未被更改。

我所知道的大多数语言都是按值传递的。通过名字传递对我来说是陌生的。

并不是说你不能传递引用,而是在引用透明的情况下,引用和值之间没有程序员可见的区别,因为你'不允许更改引用指向的内容。这使得在纯函数式编程中到处传递共享引用实际上更安全、更普遍。从语义的角度来看,它们也可能是值。