声明Lisp函数"纯粹"的能力是否有益?

Kei*_*yne 8 lisp computer-science proof compiler-optimization purely-functional

我最近已经阅读了很多关于Haskell的内容,以及它作为一种函数式语言所带来的好处.(我对讨论Lisp的monad不感兴趣)对我来说(至少在逻辑上)尽可能地隔离具有副作用的函数是有意义的.我已经充分使用了setf其他破坏性功能,并且我认识到它们在Lisp和(大多数)其衍生物中的需要.

开始了:

  1. 是否有(declare pure)可能帮助优化编译器?或者这是一个有争议的问题,因为它已经知道了?
  2. 声明是否有助于证明一个函数或程序,或者至少是一个被声明为纯粹的子集?或者这又是不必要的东西,因为它对程序员和编译器以及证明者来说已经很明显了?
  3. 如果没有别的,程序员是否有用对于编译器来强制执行此声明的函数的纯度并增加Lisp程序的可读性/可维护性?
  4. 这有什么意义吗?或者我太累了甚至不想现在?

我很感激这里有任何见解.欢迎提供有关编译器实现或可证明性的信息.

编辑

为了澄清,我不打算将这个问题限制在Common Lisp中.它显然(我认为)不适用于某些衍生语言,但我也很好奇其他Lisps的某些功能是否倾向于支持(或不支持)这种设施.

Eli*_*lay 7

你有两个答案,但没有触及真正的问题.

首先,是的,知道一个函数是纯粹的,显然会很好.有很多编译器级别的东西需要知道,以及用户级别的东西.鉴于lisp语言非常灵活,你可以稍微扭曲一下:而不是要求编译器更努力地尝试的"纯"声明,你只需要声明限制定义中的代码.这样你就可以保证功能是纯粹的.

你甚至可以通过额外的支持工具来实现这一点 - 我在johanbev的回答中提到了两条评论:添加了不可变绑定和不可变数据结构的概念.我知道在Common Lisp中这些是非常有问题的,特别是不可变的绑定(因为CL通过"副作用"加载代码到位).但是这些功能将有助于简化事情,并且它们并不是不可思议的(例如,参见具有不可变对和其他数据结构的Racket实现,并且具有不可变的绑定.

但真正的问题是你能在这种有限的功能中做些什么.即使一个非常简单的问题也会出现问题.(我正在使用类似Scheme的语法.)

(define-pure (foo x)
  (cons (+ x 1) (bar)))
Run Code Online (Sandbox Code Playgroud)

似乎很容易告诉这个功能确实是纯粹的,它什么都不做.此外,似乎define-pure限制主体并且仅允许纯代码在这种情况下可以正常工作,并且将允许此定义.

现在从问题开始:

  1. 它在呼唤cons,所以它假设它也是纯粹的.另外,正如我上面提到的,它应该依赖于cons它是什么,所以假设cons绑定是不可变的.很容易,因为它是一个已知的内置.做相同的bar,当然.

  2. cons 确实有副作用(即使你谈论的是Racket的不可变对):它分配了一对新的.这似乎是一个次要且可忽略的观点,但是,例如,如果你允许这些东西出现在纯函数中,那么你将无法自动记忆它们.问题是有人可能依赖于每foo一个回复新对的呼叫 - 一个不是 - eq对任何其他现有对.看起来要做得很好,你需要进一步限制纯函数,不仅要处理不可变值,还要处理构造函数不总是创建新值的值(例如,它可以使用hash-cons而不是allocate).

  3. 但是这个代码也会调用bar- 所以不需要bar对它做出相同的假设:它必须被称为纯函数,具有不可变的绑定.特别注意,不bar接受任何参数 - 因此在这种情况下,编译器不仅要求它bar是纯函数,它还可以使用该信息并预先计算其值.毕竟,没有输入的纯函数可以简化为普通值.(注意BTW,Haskell没有零参数函数.)

  4. 这带来了另一个重大问题.如果bar一个输入的功能怎么办?在这种情况下,你会有一个错误,并会抛出一些异常......而且这不再是纯粹的.例外是副作用.您现在需要知道bar除了其他所有内容之外的其他内容,并且您需要避免其他异常.现在,那个输入怎么样x- 如果它不是一个数字会怎么样?这也会引发异常,所以你也需要避免它.这意味着您现在需要一个类型系统.

  5. 更改(+ x 1)(/ 1 x),你可以看到,你不仅需要一个类型的系统,你需要一个足够复杂的区分0.

  6. 或者,您可以重新思考整个事情,并拥有永远不会抛出异常的新纯算术运算 - 但是除了所有其他限制之外,您现在离家很远,语言完全不同.

  7. 最后,还有一个副作用仍然是PITA:如果定义bar(define-pure (bar) (bar))什么呢?根据上述所有限制,这当然是纯粹的......但是分歧是副作用的一种形式,所以即使这不再是犹太教.(例如,如果你确实让你的编译器优化了nullary函数的值,那么对于这个例子,编译器本身会陷入无限循环.)(是的,Haskell没有处理它,它没有成功少了一个问题.)