gpt4 book ai didi

javascript - 这个带有可变引用参数的 JavaScript 函数是纯函数吗?

转载 作者:行者123 更新时间:2023-12-03 21:43:50 26 4
gpt4 key购买 nike

我和 this one 有同样的问题,但在 JavaScript 的上下文中。
来自 Wikipedia :

[a pure function's] return value is the same for the same arguments


那里进一步声称,纯函数不允许使用“可变引用参数”来改变返回值。在 JavaScript 中,每个普通对象都作为“可变引用参数”传递。考虑以下示例:
const f = (arr) => arr.length

const x = []
console.log( f(x) ) // 0
x.push(1);
console.log( f(x) ) // 1
以上证明 f不纯?
或者你会争辩说我们没有调用 f在这两种情况下使用“相同”的论点?
我知道调用 f 是多么有意义。 f 时其他线程可能会混淆可变引用参数的语言/环境中的不纯正在执行。但是自从 f不是 async ,这是不可能发生的。 x从那一刻起将保持不变 f执行完毕后调用。 (如果我理解正确,这种解释似乎得到了 Verifiable Functional Purity in Java § 4.1 中提出的“相同”定义的支持。)
还是我错过了什么? JavaScript 中是否有一个示例,其中不包含异步代码的函数会丢失 referential transparency 的属性仅仅因为它采用可变引用,但如果我们使用例如它会是纯粹的。取而代之的是 Immutable.js 数据结构?

最佳答案

当服用Wikipedia definition从字面上看,将可变数据结构(例如 native 数组)的引用作为参数的函数不是纯粹的:

Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).


等价
尽管这清楚地表明“没有可变引用参数的变化”,但我们可以说这是可以解释的,并且取决于“相同”和“变化”的含义。可能有不同的定义,因此我们进入意见领域。从您提到的论文中引用:

There is not a single obviously right answer to these questions. Determinism is thus a parameterized property: given a definition of what it means for arguments to be equivalent, a method is deterministic if all calls with equivalent arguments return results that are indistinguishable from within the language


在同一篇论文中提出的功能纯度使用以下等价定义:

Two sets of object references are considered equivalent if they result in identical object graphs


因此,使用该定义,以下两个数组被认为是等效的:
let a = [1];
let b = [1];
但是如果不增加更多限制,这个概念就不能真正应用于 JavaScript。也不是 Java,这就是论文作者提到一种精简的语言 Joe-E 的原因:

objects have identity: conceptually, they have an “address”, and we can compare whether two object references point to the same “address” using the == operator. This notion of object identity can expose nondeterminism.


用 JavaScript 说明:

const compare = (array1, array2) => array1 === array2;

let arr = [1];
let a = compare(arr, arr);
let b = compare(arr, [1]);
console.log(a === b); // false

由于这两个调用返回不同的结果,即使参数具有相同的形状和内容,我们应该得出结论(使用此等价定义)上述函数 compare不纯。在 Java 中,您可以影响 == 的行为。运算符(Joe-E 禁止调用 Object.hashCode ),因此要避免这种情况发生,这在 JavaScript 中比较对象时通常是不可能的。
意外的副作用
另一个问题是 JavaScript 不是强类型的,因此函数不能确定它接收到的参数是它们想要的。例如,以下函数看起来很纯粹:
const add = (a, b) => a + b;
但它可以以产生副作用的方式调用:

const add = (a, b) => a + b;

let i = 0;
let obj = { valueOf() { return i++ } };
let a = add(1, obj);
let b = add(1, obj);
console.log(a === b); // false

您的问题中的函数存在同样的问题:

const f = (arr) => arr.length;

const x = { get length() { return Math.random() } };
let a = f(x);
let b = f(x);
console.log(a === b) // false

在这两种情况下,该函数都无意中调用了一个不纯函数并返回了一个依赖于它的结果。虽然在第一个示例中,仍然很容易使用 typeof 使函数成为纯函数。检查,这对您的功能来说不那么微不足道。我们可以想到 instanceofArray.isArray ,甚至是一些聪明的 deepCompare函数,但是,调用者仍然可以设置一个奇怪的对象的原型(prototype),设置它的构造函数属性,用 getter 替换原始属性,将对象包装在代理中,......等等等等,甚至可以欺骗最聪明的相等检查器。
实用主义
就像在 JavaScript 中一样,有太多“松散的结尾”,为了对“纯”有一个有用的定义,必须务实,否则几乎没有什么可以被标记为纯的。
例如,在实践中,许多人会调用像 Array#slice 这样的函数。纯,即使它存在上述问题(包括与特殊参数 this 相关)。
结论
在 JavaScript 中,当调用纯函数时,您通常必须就如何调用函数达成一致。参数应该是某种类型,并且没有可以调用但不纯的(隐藏)方法。
有人可能会争辩说,这与“纯”背后的理念背道而驰,“纯”应该只由函数定义本身决定,而不是最终可能被调用的方式。

关于javascript - 这个带有可变引用参数的 JavaScript 函数是纯函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65872468/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com