评估\ _ - >在Haskell中未定义

时间:2013-04-16 15:35:08

标签: haskell

我一直在阅读http://www.haskell.org/haskellwiki/Hask。我正在努力解决这个问题。

undef1 = undefined :: a -> b
undef2 =  \_ -> undefined

为什么他们这样做..

seq undef1 () = undefined
seq undef2 () = ()
undef2 () = undefined

这是什么原因?我想了解这种行为,但我甚至不知道从哪里开始。特别是,为什么undef2在严格评估下表现不同?

4 个答案:

答案 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 - 也就是说,评估类型的值可能会导致执行无限循环或抛出异常(通常使用errorundefined)。在我们评估seq a b之后,我们可以确定将a评估为WHNF不会导致无限循环或异常。

有了这些知识,让我们来看看你的例子:

undef1 = undefined :: a -> b
undef2 =  \_ -> undefined

评估seq undef1 ()时,seq首先会尝试找出undef1上面的三个类别中的哪一个属于。{1}}。但undef1undefined,因此整个表达式的计算结果为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 -> bseq undef1 ()之间的区别仅在于,只有第二个成功强制执行第一个参数,但它只返回seq undef2 ()

如果我对上述代码的意图是正确的,那么您正在寻找的是(),它将第一个参数应用于第二个参数并强制执行结果。所以这两个都失败了($!) :: (a -> b) -> a -> b

undefined