昨天我参加了StackOverflow Dev Days大会,其中一位演讲者正在谈论Python。他展示了一个Memoize函数,我问是否有办法防止它被用于非纯函数。他说不,这基本上是不可能的,如果有人想办法做到这一点,那将会成为一个伟大的博士论文。
这让我感到困惑,因为对于编译器/解释器来说,递归求解似乎并不困难。在伪代码中:
function isPure(functionMetadata): boolean;
begin
result = true;
for each variable in functionMetadata.variablesModified
result = result and variable.isLocalToThisFunction;
for each dependency in functionMetadata.functionsCalled
result = result and isPure(dependency);
end;
这是基本的想法。显然,您需要进行某种检查以防止相互依赖的函数无限递归,但这并不难设置。
带有函数指针的高阶函数可能会有问题,因为它们无法静态验证,但我的原始问题预先假定编译器有某种语言约束来指定只能将纯函数指针传递给某个参数。如果存在,那可以用来满足条件。
显然这在编译语言中比在解释的语言中更容易,因为所有这些数字运算都将在程序执行之前完成,因此不会减慢任何速度,但我真的没有看到任何基本问题。让它无法评估。
在这个领域有更多知识的人是否知道我缺少什么?
答案 0 :(得分:10)
您还需要注释每个系统调用,每个FFI,......
此外,最微小的“泄漏”往往泄漏到整个代码库中。
这不是一个理论上难以处理的问题,但在实践中,以一种整个系统不会感到脆弱的方式进行是非常困难的。
顺便说一句,我认为这不是一篇优秀的博士论文; Haskell实际上已经有了这个版本,并且有IO monad。
我相信很多人会继续在实践中看到这个。 (疯狂猜测)20年后我们可能会有这个。
答案 1 :(得分:5)
Python中特别难。由于anObject.aFunc
可以在运行时任意更改,因此您无法在编译时确定将anObject.aFunc()
调用哪个函数,或者即使它将是一个函数。
答案 2 :(得分:4)
除了这里的其他优秀答案之外:您的伪代码仅查看函数是否修改变量。但这并不是“纯粹”的意思。 “纯粹”通常意味着更接近“引用透明”的东西。换句话说,输出完全取决于输入。所以简单到读取当前时间并将其作为结果中的一个因素(或从输入读取,或读取机器的状态,或......)使得函数非纯粹而不修改任何变量。
另外,你可以编写一个修改变量的“纯”函数。
答案 3 :(得分:3)
当我读到你的问题时,这是我脑海中浮现的第一件事。
类层次结构
确定变量是否被修改包括挖掘在变量上调用的每个方法以确定它是否在变异的行为。对于具有非虚方法的密封类型,这是......有点直接。
但考虑虚拟方法。您必须找到每个派生类型,并验证该方法的每个重写都不会改变状态。在允许动态代码生成的任何语言/框架中确定这是不可能的,或者仅仅是动态的(如果可能的话,这是非常困难的)。原因是派生类型集不是固定的,因为可以在运行时生成新的派生类型。
以C#为例。没有什么能阻止我在运行时生成派生类,它会覆盖该虚拟方法并修改状态。静态验证将无法检测到此类修改,因此无法验证方法是否纯粹。
答案 4 :(得分:0)
我认为主要问题是如何有效地做到这一点。
D语言具有纯函数,但您必须自己指定它们,因此编译器会知道检查它们。我想如果你手动指定它们那么它会更容易。
答案 5 :(得分:0)
一般来说,确定给定函数是否纯粹是可以简化的,以决定任何给定的程序是否会停止 - 众所周知,停机问题是一种无法有效解决的问题。
答案 6 :(得分:0)
请注意,复杂性也取决于语言。对于更动态的语言,可以随时重新定义任何内容。例如,在Tcl
中proc myproc {a b} {
if { $a > $b } {
return $a
} else {
return $b
}
}
每一件都可以随时修改。例如:
不可否认,Tcl是一个极端的案例;有一种最动态的语言。话虽如此,它突出了一个问题,即即使你输入它也很难确定一个功能的纯度。