Haskell language specification states它是一种非严格的语言,但与评估策略无关(比如表达式的评估时间和方式,以及评估级别)。在谈论模式匹配时,它确实多次提到“评估”这个词。
我已经阅读了关于延迟评估和弱头正常形式的精彩tutorial,但它只是某些编译器的实现策略,在编写代码时我不应该依赖它。
我来自严格的语言背景,如果我不理解我的代码是如何执行的,我感觉不对。我想知道为什么语言规范没有定义评估策略。
我希望有人能够启发我。谢谢!
答案 0 :(得分:9)
我认为,在Haskell中尝试关注评估顺序会产生相反的效果。该语言不仅旨在使评估顺序无关紧要,而且评估顺序可以以奇怪和混乱的方式遍布整个地方。此外,如果最终结果仍然相同,则实现具有以不同方式执行事物的大量自由[1]或大量重构程序[2],因此可能以不同方式评估程序的不同部分。
唯一真正的限制是您无法使用的评估策略。例如,您不能总是使用严格的评估,因为这会导致有效程序崩溃或进入无限循环。
const 17 undefined
take 10 (repeat 17)
那就是说,如果你真的关心,你可能用来实现所有Haskell的一个有效策略就是使用thunk进行懒惰的评估。每个值都在一个框中表示,该框包含值或thunk子例程,可用于在最终需要使用它时计算该值。
所以当你写
let xs = 1 : []
你会这样做:
x --> {thunk}
如果你从不检查x的内容,那么thunk保持不被评估。但是,如果您在thunk上进行了一些模式匹配,那么您需要评估thunk以查看您采用的分支:
case xs of
[] -> ...
(y:ys) -> ...
现在,评估x的thunk并将结果值存储在框中以备不时之需。这是为了避免需要重新计算thunk。请注意,在下图中,我使用Cons
代表:
列表构造函数
x ---> {Cons {thunk} {thunk}}
^ ^
| |
y ys
当然,只是存在模式数学是不够的,首先需要首先强制评估该模式匹配。最终,这归结为需要打印值或类似值的主要功能。
另一点需要指出的是,当我们第一次评估它时,我们没有立即评估Cons构造函数的内容。您可以通过运行不使用列表内容的程序来检查:
length [undefined, undefined]
当然,当我们实际使用y
时,会对其相应的thunk进行评估。
此外,您可以使用爆炸模式将构造函数字段标记为严格,以便在构造函数被评估时立即进行评估(我不记得您是否需要为此转换扩展名)
data LazyBox = LazyBox Int
data StrictBox = StrictBox !Int
case (LazyBox undefined) of (LazyBox _) -> 17 -- Gives 17
case (StrictBox undefined) of (StrictBox _) -> 17 -- *** Exception: Prelude.undefined
[1]:GHC所做的一个重要优化是在严格性分析器确定严格的部分中使用严格的评估。
[2]:最激进的例子之一是deforestation。