有没有使用严格求值的Haskell编译器或预处理器?

19

我正在寻找一个使用严格求值的哈斯克尔编译器,而不是惰性求值。我本可以使用OCaml,但Haskell的语法比OCaml好得多(而且Haskell是纯净的,具有类型类等酷炫功能)。

我真的不想在我的程序中到处都加上!$!。如果有一个开关或预处理器来放置严格性注释,那将非常好。如果有一种方法在某些地方使用惰性求值,以防我需要像无限列表这样的东西(虽然我可能永远不会需要),那也将很有帮助。

请不要试图说服我惰性求值更好,我真的需要性能。如果我没记错,Simon Peyton Jones甚至说过,惰性求值并不是必需的,它主要是为了防止使语言变得不纯。


1
如果存在这样的预处理器(我不知道),那么它可能意味着您必须重新编译使用的每个库,因为它们都是惰性的(并且编写为在惰性环境中工作)。我猜测,如果突然使用严格评估来使用大多数Haskell库,它们中的大部分都会出现问题。 - Tom Lokhorst
@Tom Lokhorst:当然有些东西可能会出问题,但我预计大多数东西在不经过修改的情况下应该能正常工作。 - Zifre
6
你有懒惰求值导致性能极差的例子吗? - ShreevatsaR
2
当然可以。这可能与我的计算机只有6GB内存和空间泄漏有关。如果您愿意签署保密协议,下次出现这种情况,我非常乐意让您帮忙。 - cjs
我觉得我可能无法帮忙,无论是否签订保密协议。我本来希望从您的例子中学到一些东西,但是一个纯粹存在的陈述并不是很有启发性。 :-) - ShreevatsaR
@ShreevatsaR:请参考这个Stack Overflow问题,了解一个非常简单的例子,说明为什么这很重要:(https://dev59.com/b3RC5IYBdhLWcg3wFdFx)。懒惰求值背后的运作并不总是显而易见。即使是尾递归,也会导致堆栈溢出! - Zifre
11个回答

17

我真的不想在我的程序中到处加上感叹号和$符号

如果您是这样编程 Haskell ,那么您正在错误地进行编程 :) 您根本不需要这样做。使用 GHC,使用 -O2,适当时使用严格数据类型,适当时使用惰性数据类型。不要假设惰性会成为问题——它是许多问题的解决方案。


16
如果您使用严格求值的Haskell编译器,则它不会编译Haskell。非严格性是Haskell规范的一部分!然而,有替代方案。
- DDC试图创建一个明确的惰性Haskell变体,支持破坏性更新,同时保留Haskell的所有优点。但存在一个问题:编译器目前仅处于α阶段,尽管似乎至少可用。 - 像其他人一样创建预处理器。 - 学会正确使用Haskell。如果您可以将测试用例简化为可公开显示的内容,则可以在Haskell-Café邮件列表上发布它,在那里人们非常乐意帮助解决这些关于非严格性影响的问题。

1
DDC看起来非常不错。现在我必须掌握效果系统而不是单子... - Zifre
4
准确地说,非严格性是 Haskell 规范的一部分,而不是惰性。惰性只是 GHC 选择的求值顺序。 - Jake McArthur

12

过去有两次尝试严格评估Haskell:

但是两者都专注于坚持Haskell的非严格语义,但使用大部分严格的评估策略,而不是真正改变语义,也从未真正被采用。

编辑:Martijn提出的“strict-plugin”对您的目的非常理想,因为它实际上做到了您想要的,并且作者仍然活跃在Haskell社区中,我之前忘记了这个。


8

7

我理解你的痛苦。在我的日常编程中,最让人头疼的问题就是处理那些令人恼火的空间泄漏。

然而,如果有帮助的话,随着时间的推移,你会学会(通过艰辛的方式)如何处理这个问题,并且情况会变得更好。但我仍在等待Andy Gill发布他神奇的空间泄漏分析器来解决我所有的问题。(我把他在上一次ICFP对我说的这个想法当作了他的承诺来实现它。)

我不会试图说懒惰求值是世界上最好的东西,但它确实有某些优点。我有一些流处理程序,可以轻松地将懒惰列表通过各种组合器进行处理,同时只使用3.5MB左右的内存(其中超过2MB是GHC运行时)。去年有比我聪明的人向我指出,作为一个典型的Haskell程序员,你会非常惊讶于你依赖懒惰求值的程度。

但我们真正需要的是一本关于如何在现实世界中处理懒惰求值的好书(其实并没有太大区别,除了他们无法发表论文,而我们则会有客户拿着刀追杀我们),这本书将适当地涵盖大部分与此相关的问题,并更重要的是,给我们一种直观的感觉,知道什么会导致堆栈溢出,什么不会。

我认为这不是一个新问题;我相信其他语言和架构也经历过这个问题。毕竟,第一代程序员如何处理硬件堆栈等问题呢?我敢打赌他们做得并不好。


2
顺便说一句,你可以借助模板Haskell使各种事物成为“NFData”的实例,并且可以多次强制执行。我曾经吃过亏,发现这不是除了真正让CPU缓存溢出之外的最佳解决方案... - cjs

5

5

在所有地方都使用nfdata和rnf并不是一个解决方案,因为这意味着要重复遍历已经被评估的大型结构。

Ben Lippmeier的博士论文(关于DDC)的介绍性章节是我见过的对Haskell最好的批判 - 它讨论了惰性、破坏性更新、单子变换器等问题。DDC具有惰性,但您必须显式请求它,并且它被视为一种效果,由DDC的类型和效果系统跟踪和管理。


5

我认为Jan-Willem Maessan的pH编译器是/曾经很严格。接近它的是Robert Ennal为ghc 5开发的投机评估分支。spec_eval分支不是严格的,而是进行乐观评估。我不知道这两者是否仍然当前/可用等。


1
我正在寻找一个使用严格求值而不是惰性求值的Haskell编译器。这样的编译器将不会是一个Haskell编译器。如果你真的想要,你可以考虑在你的文件中放置“{-# LANGUAGE Strict #-}”指示语句。这将与GHC 8.0.2、8.2.2和8.4.1一起工作,也就是编译器的最后三个版本。
如果有一种方法可以在某些地方使用惰性求值,那将非常有帮助,以防我需要像无限列表之类的东西。
没有这样的方法。相反,使用GHC的方式 - 作为一种惰性语言。学习思考你的代码,进行分析,并正确使用函数数据结构将比无脑地在所有地方应用严格性指示语句更有用。 GHC已经有了一个严格性分析器。
(我可能永远不会这样做)。
这正是llvm-hs作者在选择使用严格状态单子而非惰性单子时所考虑的原因,然而这却导致了一个意外的 bug。惰性求值和递归是相辅相成的。

请不要试图说服我惰性求值更好,我真的需要性能。

我对此表示怀疑,因为它并不能可靠地提高 Haskell 代码的性能,同时还会破坏现有的代码和使现有资源无用。如果您打算以这种方式编写程序,请使用 OCaml 或 Scala,不要打扰 Haskell 社区。

如果我没记错的话,Simon Peyton Jones 甚至说过惰性求值并不是必需的,它主要是为了防止让语言变得不纯。

那不是真的。您可以在这里阅读更多有关 Haskell 的实际历史。

0

还有seqaid,它旨在处于懒惰-严格谱系的中间。

Seqaid是一个GHC插件,为Haskell项目提供非侵入式自动仪器化,用于动态严格性(和并行性)控制。这很快将包括使用最小限度的严格化进行自动空间泄漏缓解的优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接