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
已定义的底部特殊情况。
或许还有其他一些我不知道的联系。
这就是我所要求的:)
答案 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
评估的实现。但它看起来像一个更复杂的解决方案,可能还有其自身的陷阱。