我一直在阅读http://www.haskell.org/haskellwiki/Hask。我正在努力解决这个问题。
undef1 = undefined :: a -> b
undef2 = \_ -> undefined
为什么他们这样做..
seq undef1 () = undefined
seq undef2 () = ()
undef2 () = undefined
这是什么原因?我想了解这种行为,但我甚至不知道从哪里开始。特别是,为什么undef2在严格评估下表现不同?
答案 0 :(得分:9)
特殊函数seq
将其第一个参数计算为弱头正规形式,然后返回其第二个参数。可以找到WHNF的一个很好的解释here,但为了这个答案的目的,我将只使用Haskell wiki definition:
表达式是弱头正常形式(WHNF),如果它是:
- 一个构造函数(最终应用于参数),如True,Just(square 42)或(:) 1
- 一个函数应用于太少的参数(可能没有),如(+)2或sqrt。
- 或lambda abstraction \ x - >表达
重要的一点是,当表达式是构造函数时,seq
只查看构造函数的标记。因此,seq (Just undefined) 1
评估为1
。
另一个要点是Haskell中的所有类型都是lifted - 也就是说,评估类型的值可能会导致执行无限循环或抛出异常(通常使用error
或undefined
)。在我们评估seq a b
之后,我们可以确定将a
评估为WHNF不会导致无限循环或异常。
有了这些知识,让我们来看看你的例子:
undef1 = undefined :: a -> b
undef2 = \_ -> undefined
评估seq undef1 ()
时,seq
首先会尝试找出undef1
上面的三个类别中的哪一个属于。{1}}。但undef1
为undefined
,因此整个表达式的计算结果为undefined
。
但是,在seq undef2 () = ()
的情况下,第一个参数是lambda抽象。由于seq
无法看到lambda,因此返回第二个参数。
第三个示例undef2 () = undefined
只是评估应用程序(\_ -> undefined) ()
的直接结果。
答案 1 :(得分:3)
他们不是一回事。 undef1是从a到b的函数,但是哪个函数是未定义的。评估undef1到头部正常形式会给你未定义。
undef2是从a到b的函数。具体来说,它是一个忽略其参数并返回undefined的函数。但是undef2本身并没有被定义。只有当您尝试评估函数时(如在第三行中),您才会得到未定义。因此,当您将undef2评估为正常形式时,您将得到一个正确的函数,而不是未定义的函数。
将它置于更加命令性的术语中(总是不准确的来源,但如果你对它更熟悉,它很好地阐明了这一点),将undef1视为永不返回的属性获取者。 (在Haskell中,不返回和未定义在语义上等效。)undef2是一个返回函数的属性getter;如果你打电话,这个功能将不会返回。在C#中:
Func<Object, Object> Undef1 {
get {
while (true) {}
return null;
}
}
Func<Object, Object> Undef2 {
get {
return _ -> {
while (true) {}
return null;
}
}
}
现在您的测试成为:
var x = Undef1;
var y = Undef2;
var z = Undef2(null);
答案 2 :(得分:3)
为了这个问题,让我们说有三件事可以强制实际评估一个值:
seq
实际情况稍微复杂一些,但在这里并不重要。
此外,这种强制只发生在一些外部表达的上下文中,所以不要将它们视为以某种抽象的方式“强迫评估”,而是将它们视为使得外部表达式的评估依赖于评估该价值。这就是为什么,例如seq x x
在任何意义上都不会强制x
,因为这无论如何都是表达式的最终值;它说当评估外部表达式(其值为x
)时,它还应该评估x
,这是多余的。
最后,任何依赖于强制未定义值的值本身都是未定义的。
浏览每个表达式:
seq undef1 () = undefined
在这种情况下,undef1
未定义,seq undef1 x
是一个值为x
且依赖于评估undef1
的表达式。因此,无论seq
的第二个参数是什么,整个表达式都是未定义的。
seq undef2 () = ()
在这种情况下,undef2
不未定义,但应用它的结果是。 seq undef2 x
是一个值为x
的表达式,取决于评估undef2
。这不会导致任何问题,seq
的第一个参数将被丢弃,因此表达式的值为()
。
undef2 () = undefined
在这种情况下,我们直接应用undef2
。表达式undef2 ()
取决于undef2
的评估(这很好)并评估应用undef2
的结果,在这种情况下为undefined
。
将此与第四种情况进行对比:
undef1 () = undefined
在这种情况下,我们应用undef1
,因此表达式的值取决于评估undef1
,这是未定义的,因此整个表达式也是如此。这与使用undef2
的上一个表达式具有相同的“值”,但原因却截然不同!
答案 3 :(得分:3)
好吧,只是写一个比其他人更简洁的答案:为了强制评估像f x
这样的表达式,Haskell首先要弄清楚函数f
是什么,然后应用它{{1 }}。在更多技术术语中,只要x
强制,就必须强制f x
。
现在让我们回到你的定义:
f
强制undef1 :: a -> b
undef1 = undefined
undef2 :: a -> b
undef2 = \_ -> undefined
和undef1
会产生不同的结果。强制undef2
立即产生undef1
,而强制undefined
会产生一个thunk,当应用于某个参数时,会产生undef2
。
我怀疑你误解了undefined
的作用;我认为您认为seq
应该seq undef2 ()
应用于undef2
,但它不会这样做。 ()
做的是强制它的第一个参数并返回第二个参数。 seq :: a -> b -> b
和seq undef1 ()
之间的区别仅在于,只有第二个成功强制执行第一个参数,但它只返回seq undef2 ()
。
如果我对上述代码的意图是正确的,那么您正在寻找的是()
,它将第一个参数应用于第二个参数并强制执行结果。所以这两个都失败了($!) :: (a -> b) -> a -> b
:
undefined