为什么Haskell中的seq必须有底部的特殊规则?

时间:2014-10-31 07:59:20

标签: haskell

Haskell Report 2010表示seq“削弱了Haskell的参数性质”,因为“⊥与\ x - > gt不同,因为seq可用于区分它们”[1]。

这似乎完全是因为一个明确的规则:

seq ⊥ b  =  ⊥

我想知道为什么会引入这个特例?

必须有原因
seq a b  =  b

是不够的,seq需要一个更复杂的定义:

seq ⊥ b  =  ⊥
seq a b  =  b, if a ≠ ⊥ 

[1] https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1260006.2


[编辑]澄清问题,这是另一个角度。 为什么seq不能像这样定义:

seq :: a -> b -> b
seq a b  =  b
  

规则seq是一个特殊的函数,可以“尽可能地”评估它的第一个参数。如果评估结果为HNF,则完全评估参数。如果它会导致异常或最低值,那么只要第一个参数实际用于第二个参数的评估,它们就会被存储并被抛出或返回。

这有点蹩脚,但我认为这应该让我的问题更加明确。这与seq的工作原理无关。这是关于当前设计的意图。

从实施的角度来看,也许有一些明显的原因。或者它可能会产生一些后果,例如无法提供一些有用的属性,这些属性当前基于seq已定义的底部特殊情况。 或许还有其他一些我不知道的联系。 这就是我所要求的:)

2 个答案:

答案 0 :(得分:15)

seq x y的重点是评估 x,然后返回y。 Haskell很懒,所以定义如

seq x y = y

不会这样做,因为x未被评估。如果我们知道x :: Int我们可以写

seq x y = case x of
          0 -> y
          _ -> y

强制x进行评估。我们可以为列表,树等播放相同的技巧,但有一个重要的例外: functions 。如果x是一个函数,我们就不能case超过它(除了琐碎的模式,没有强制评估)。功能仅在通话时评估,因此我们可能会尝试

seq x y = case x 12 of -- let's pretend it's an Int->Int function
          0 -> y
          _ -> y

这会强制评估x(好!),但也会评估x 12(不好!)。

事实证明,我们知道在lambda演算中写seq实际上是不可能的:理论将直觉形式化为“我们不能强制执行函数,除非我们申请,如果我们申请我们也强迫结果“。

因此,在Haskell中,seq被添加为原始操作,而不是根据其他所有操作来定义。 seq的实现对其第一个参数是严格的,即使没有必要对它进行评估以返回第二个参数。

由于seq在lambda演算之外做了一些事情,它破坏了理论中的一些东西,比如参数化,但这种损失并没有破坏整个语言,它仍然享有许多好处lambda演算的理论性质。

从历史上看,如果我的记忆对我有用,那么早期的Haskell即将包含seq的一个类:

 class Seq a where
    seq :: a -> b -> b

并且此类在每个类型上实例化,但函数,因此也保留了参数。后来,决定在代码中添加所有Seq a约束是不切实际的,因为单个使用seq需要添加大量约束。所以,seq被制成了一个原语,其代价是破坏了理论。

答案 1 :(得分:2)

上面的答案包含一个讨论,最终让我明白我错过了什么。我认为以下是它的要点。

如果我们仅限于一个线程,则建议的定义需要解决暂停问题。 ⊥表示3种不同的东西:异常,错误和非终止。可以处理前两个,但在一般情况下,第三个是不可能解决的。

可能会提供一个使用额外线程来运行a评估的实现。但它看起来像一个更复杂的解决方案,可能还有其自身的陷阱。