>在Haskell中未定义
我一直在阅读这个http://www.haskell.org/haskellwiki/Hask。 我正在努力与这部分..
undef1 = undefined :: a -> b
undef2 = _ -> undefined
为什么他们的行为像这样..
seq undef1 () = undefined
seq undef2 () = ()
undef2 () = undefined
这是什么原因? 我想了解这种行为,但我甚至不知道从哪里开始。 特别是,为什么undef2在严格评估下表现不同?
特殊函数seq
其第一个参数评估为弱头标准形式,然后返回其第二个参数。 WHNF的一个很好的解释可以在这里找到,但为了这个答案的目的,我将使用Haskell的wiki定义:
如果它是以下任一情况,则表达式处于弱头标准形式(WHNF):
重要的一点是,当一个表达式是一个构造函数时, seq
只会查看构造函数的标签。 因此, seq (Just undefined) 1
计算结果为1
。
另一个重要的一点是,Haskell中的所有类型都被解除了 - 也就是说,评估类型的值可能会导致无限循环被执行或引发异常(通常出现error
或undefined
)。 在我们评估过seq ab
,我们可以肯定,评估a
WHNF不会导致无限循环或异常。
有了这些知识,让我们看看你的例子:
undef1 = undefined :: a -> b
undef2 = _ -> undefined
当seq undef1 ()
被评估时, seq
首先尝试找出undef1
上面三个类别中的undef1
属于。 但是undef1
是undefined
,所以整个表达式评估为undefined
。
但是,在seq undef2 () = ()
的情况下,第一个参数是lambda抽象。 由于seq
无法看到lambda,它返回第二个参数。
第三个例子undef2 () = undefined
只是评估应用程序的简单结果(_ -> undefined) ()
。
他们不是一回事。 undef1是从a到b的函数,但哪个函数是未定义的。 评估undef1头正常的形式让你不确定。
undef2是从a到b的函数。 具体来说,它是一个忽略其参数并返回未定义的函数。 但是undef2本身并不是未定义的。 只有当你尝试评估函数时(如你的第三行),你会得到未定义的。 所以,当你评估undef2头正常形式,你会得到一个正确的功能,而不是未定义的。
把它放在更加迫切的术语中(总是不准确的来源,但如果你更熟悉这一点,它很好地说明了这一点),将undef1看作是永不返回的属性获取器。 (在Haskell中,不返回和undefined在语义上是等价的。)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);
为了这个问题,我们假设有三件事情可以迫使一个价值被实际评估:
seq
的第一个参数 实际情况稍微复杂一些,但这不是一个重要的方式。
此外,这种强迫只发生在某种外在表达的背景下,因此,不要把这些看作是以某种抽象的方式“强迫评估”,而是有助于将它们视为对外部表达的评估取决于评估值。 这就是为什么,例如, seq xx
在任何意义上都不强制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
的结果,在这种情况下undef2
是undefined
。
与第四种情况对比:
undef1 () = undefined
在这种情况下,我们应用undef1
,因此表达式的值取决于评估undef1
, undef1
是未定义的,因此整个表达式也是如此。 这与使用undef2
的前一个表达式是相同的“值”,但出于完全不同的原因!
下一篇: Does a function in Haskell always evaluate its return value?