为什么纯函数式语言只允许按值传递参数?
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 中得到一个新的字符串对象,并且原始对象未被更改。
我所知道的大多数语言都是按值传递的。通过名字传递对我来说是陌生的。
并不是说你不能传递引用,而是在引用透明的情况下,引用和值之间没有程序员可见的区别,因为你'不允许更改引用指向的内容。这使得在纯函数式编程中到处传递共享引用实际上更安全、更普遍。从语义的角度来看,它们也可能是值。
我是函数式语言的新手,我想知道为什么我们不能通过引用传递参数。 我发现 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 中得到一个新的字符串对象,并且原始对象未被更改。
我所知道的大多数语言都是按值传递的。通过名字传递对我来说是陌生的。
并不是说你不能传递引用,而是在引用透明的情况下,引用和值之间没有程序员可见的区别,因为你'不允许更改引用指向的内容。这使得在纯函数式编程中到处传递共享引用实际上更安全、更普遍。从语义的角度来看,它们也可能是值。