当我们可以假设纯度和参考透明度时,通常的好处就适用。我们可以自动记忆热点。我们可以自动并行计算。我们可以处理很多竞争条件。我们还可以对我们知道不能修改的数据使用结构共享,例如(准)原语“cons()”不需要复制它所连接到的列表中的 cons-cells。这些单元格不会受到另一个指向它的 cons-cell 的影响。这个例子有点明显,但编译器在解决更复杂的结构共享方面通常表现出色。
然而,在 Common Lisp 中,实际确定一个 lambda(一个函数)是纯的还是具有引用透明性是非常棘手的。请记住,函数调用 (foo bar) 从查看 (symbol-function foo) 开始。所以在这种情况下
(defun foo (bar)
(cons 'zot bar))
foo() 是纯粹的。
下一个 lambda 也是纯的。
(defun quux ()
(mapcar #'foo '(zong ding flop)))
但是,稍后我们可以重新定义 foo:
(let ((accu -1))
(defun foo (bar)
(incf accu)))
对 quux() 的下一次调用不再是纯粹的!旧的纯 foo() 已被重新定义为不纯的 lambda。哎呀。这个例子可能有点做作,但在词法上重新定义一些函数并不少见,例如使用 let 块。在这种情况下,不可能知道编译时会发生什么。
Common Lisp 具有非常动态的语义,因此实际上能够提前确定控制流和数据流(例如在编译时)非常困难,并且在大多数有用的情况下完全无法确定。这是具有动态类型系统的语言的典型特征。如果您必须使用静态类型,那么 Lisp 中有很多常见的习惯用法是您无法使用的。主要是这些破坏了任何进行有意义的静态分析的尝试。我们可以为像 cons 和 friends 这样的原语做到这一点。但是对于涉及除原语之外的其他事物的 lambda,我们处于更深的水中,尤其是在我们需要查看函数之间复杂的相互作用的情况下。请记住,只有当它调用的所有 lambda 都是纯的时,它才是纯的。
在我的脑海中,有可能通过一些深刻的宏观学来消除重新定义的问题。从某种意义上说,每个 lambda 都有一个额外的参数,它是一个代表 lisp 图像的整个状态的 monad(我们显然可以将自己限制在函数实际查看的内容上)。但是,能够自己声明纯度可能更有用,因为我们向编译器保证这个 lambda 确实是纯净的。如果不是这样,后果是不确定的,并且可能会发生各种混乱......