这个JavaScript函数采用可变的引用参数,是纯函数吗? [英] Is this JavaScript function, taking a mutable reference argument, a pure function?

查看:51
本文介绍了这个JavaScript函数采用可变的引用参数,是纯函数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我和这个问题存在相同的问题,但是在JavaScript的背景下.

I have the same question as this one, but in the context of JavaScript.

来自维基百科:

[纯函数的]返回值与相同参数

在那里进一步要求不允许纯函数具有带有可变参考自变量"的返回值的变化.在JavaScript中,每个普通对象都作为可变引用自变量"传递.考虑以下示例:

It's further claimed there that a pure function is not allowed to have a variation in return value with "mutable reference arguments". In JavaScript, every normal object is passed as a "mutable reference argument". Consider the following example:

const f = (arr) => arr.length

const x = []
console.log( f(x) ) // 0
x.push(1);
console.log( f(x) ) // 1

以上证明 f 不纯吗?还是您会认为我们不使用相同"的名称来调用 f ?在两种情况下争论不休?

Is the above proof that f is impure? Or would you argue that we're not calling f with the "same" argument in the two cases?

我可以看到在语言/环境中调用 f 不纯是有什么意义的,因为在执行 f 时其他线程可能会混淆可变的引用参数.但是由于 f 不是 async ,因此无法发生这种情况.从调用 f 到执行完成之间, x 都将保持不变.(如果我理解正确,那么

I can see how it would make sense to call f impure in a language/environment where other threads could potentially mess with the mutable reference argument while f is executing. But since f is not async, there is no way for this to happen. x is going to stay the same from the moment f is called to when it's done executing. (If I'm understanding correctly, this interpretation seems to be supported by the definition of "same" put forth in § 4.1 of Verifiable Functional Purity in Java.)

还是我错过了什么?JavaScript中是否有一个示例,其中不包含异步代码的函数会丢失参照透明的属性因为它采用了可变的参考,但是如果我们使用例如而不是Immutable.js数据结构?

Or am I missing something? Is there an example in JavaScript where a function containing no asynchronous code loses the property of referential transparency simply because it's taking a mutable reference, but it would be pure if we used e.g. an Immutable.js data structure instead?

推荐答案

采用 Wikipedia定义字母,将可变数据结构(例如本机Array)的引用作为参数的函数不是纯净的:

When taking the Wikipedia definition to the letter, a function that takes as argument a reference to a mutable data structure (such as a native Array) is not pure:

对于相同的参数,其返回值是相同的(与本地静态变量,非本地变量,可变参考参数无变化,或者来自I/O的输入流设备).

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).

对等

尽管这清楚地表明可变参考变量没有变化",但我们可以说这是易于解释的,并且取决于""相同" 和"""variation"的含义; .可能有不同的定义,因此我们进入了观点领域.引用您所引用的论文:

Equivalence

Although this clearly says "no variation with mutable reference arguments", we could maybe say this is open to interpretation and depends on the meaning of "same" and "variation". There are different definitions possible, and so we enter the area of opinion. Quoted from the paper your referred to:

对于这些问题,没有一个明显正确的答案.因此,确定性是参数化的属性:给出了对等价参数意味着什么的定义,如果所有具有 equivalent 参数的调用返回的结果与语言内

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

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

The functional purity proposed in the same paper, uses the following definition of equivalence:

如果两组对象引用导致相同的对象图,则认为它们是等效的

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

因此,根据该定义,以下两个数组被认为是等效的:

So with that definition, the following two arrays are considered equivalent:

let a = [1];
let b = [1];

但是,如果不添加更多限制,就不能真正将这一概念应用于JavaScript.Java也是如此,这就是本文的作者引用一种精简语言(称为Joe-E)的原因:

But this concept can not really be applied to JavaScript without adding more restrictions. Nor to Java, which is the reason why the authors of the paper refer to a trimmed-down language, called 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图解:

Illustrated in 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中使用

As the two calls return a different result, even though the arguments had the same shape and content, we should conclude (with this definition of equivalence) that the above function compare is not pure. While in Java you can influence the behaviour of the == operator (Joe-E forbids calling Object.hashCode), and so avoid this from happening, this is not generally possible in JavaScript when comparing objects.

另一个问题是JavaScript不是强类型的,因此函数不能确定其接收到的参数就是它们的预期值.例如,以下函数看起来很纯净:

Another issue is that JavaScript is not strongly typed, and so a function cannot be certain that the arguments it receives are what they are intended to be. For instance, the following function looks pure:

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

但是它可以以产生副作用的方式调用:

But it can be called in way to give side effects:

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

问题中的函数存在相同的问题:

The same problem exists with the function in your question:

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 检查仍然可以使函数纯净,这对您的函数来说却并非那么简单.我们可以想到 instanceof Array.isArray ,甚至是一些智能的 deepCompare 函数,但是调用者仍然可以设置一个陌生对象的原型,它的构造函数属性,用吸气剂替换原始属性,将对象包装在代理中,...等等,等等,即使是最聪明的平等检查器也是如此.

In both cases the function unintentionally called an impure function and returned a result that depended on it. While in the first example it is easy to still make the function pure with a typeof check, this is less trivial for your function. We can think of instanceof or Array.isArray, or even some smart deepCompare function, but still, callers can set a strange object's prototype, set its constructor property, replace primitive properties with getters, wrap the object in a proxy, ...etc, etc, and so fool even the smartest equality checkers.

就像在JavaScript中那样,太多的松散结尾"必须具有实用性才能具有纯"的有用定义,否则几乎没有任何东西可以被标记为纯净的.

As in JavaScript there are just too many "loose ends", one has to be pragmatic in order to have a useful definition of "pure", as otherwise almost nothing can be labelled pure.

例如,在实践中,即使遇到上述问题(包括与特殊参数 this 相关的问题),许多人也会调用像 Array#slice 这样的纯函数.).

For example, in practice many will call a function like Array#slice pure, even though it suffers from the problems mentioned above (including related to the special argument this).

在JavaScript中,当纯函数调用时,您通常必须就如何调用该函数达成协议.参数应为特定类型,并且不具有可以调用但不纯的(隐藏)方法.

In JavaScript, when calling a function pure, you will often have to agree on a contract on how the function should be called. The arguments should be of a certain type, and not have (hidden) methods that could be called but that are impure.

有人可能会认为这与纯"(pure)背后的思想背道而驰,纯"(pure)只能由函数定义本身确定,而不是由函数最终定义的方式来确定.

One may argue that this goes against the idea behind "pure", which should only be determined by the function definition itself, not the way it eventually might get called.

这篇关于这个JavaScript函数采用可变的引用参数,是纯函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆