Haskell中“ undefined”的类型签名是什么意思?

时间:2019-05-26 07:24:02

标签: haskell types undefined forall type-signature

我是Haskell的初学者,undefined函数的类型签名使我大吃一惊。

我期望有一些更简单的方法,但是我在Hackage上发现了这一点:

undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r). HasCallStack => a
  

错误的特殊情况。预计编译器将识别出这种情况并插入更适合出现undefined的上下文的错误消息。

能否请您解释一下此签名的含义?

谢谢!

2 个答案:

答案 0 :(得分:5)

对于初学者需要知道的所有方面,签名都是

undefined :: a

这意味着,与类型变量(即,任何小写字母)一样,auniversally quantified,也可以使其明确:

{-# LANGUAGE ExplicitForall #-}
undefined :: forall a. a

...或者,我更喜欢写它

{-# LANGUAGE ExplicitForall, UnicodeSyntax #-}
undefined :: ∀ a. a

推断量化是针对所有类型,即所有类型为*的事物(阅读:“类型”,更准确的是提升类型的类型 > –解除含义,可以是懒惰的重击)。因此,无论需要什么类型,都可以在任何表达式中使用undefined

现在,undefined当然是类似“部分函数”的东西,基本上是一个零参数的函数,该参数在任何地方都没有定义。 (FTR,它不是 函数,因为根据定义,该函数具有 参数。)
您希望在实际评估时得到一条有用的错误消息,但是GHC默认情况下不会为所有内容生成调用堆栈(出于性能原因),因此它曾经错误消息几乎完全无用的情况。这就是HasCallStack出现的地方:这是一个约束,它实际上告诉某些代码可能在其中发生的上下文undefined,它应该记下发生的位置,以便错误消息可以实际显示出来起来所以,

undefined :: ∀ a. HasCallStack => a

HasCallStack之后出现∀ a有点令人困惑-这实际上与a没有任何关系,但是与undefined将使用。只是签名的形式总是

Identifier :: Quantifiers. Constraints => Type

HasCallStack是一个约束,这就是为什么它出现在中间的原因。 (通常,约束实际上适用于您已量化的类型变量之一。)

最后,这些RunTimeRep内容是关于高度多态性的。我本人并不太了解,但是Why is the undefined function levity-polymorphic when using it with unboxed types?

中对此进行了讨论。

答案 1 :(得分:5)

这是@leftaroundabout答案的延续。

在Haskell中,值具有类型。但是类型自己也有类型。当一种类型充当另一种类型的类型时,我们称其为“种类”。

在Haskell中最重要和最常见的一种是Type,通常在签名中表示为*。这是一种“提升”的类型,也就是说,其值可以是杂项,在评估时可以发散,抛出错误等。例如,类型Int的类型为{{1 }}。

还有其他类型,例如Int#不会被 取消。类型为Type的值从不是一个笨拙的东西,它始终是内存中的实际值。

简而言之,值在内存中的表示形式受其类型类型的控制。

RuntimeRep是另一种。它是Int#LiftedRep之类的类型。我们将看到,这些类型没有任何值,仅存在于express things at the type level中。

有一个称为TYPE的超魔术类型级实体,当使用类型IntRep(即描述内存中表示的类型)进行参数化时,该实体将返回该类型其值具有该表示形式的类型。例如,RuntimeRepType,而TYPE LiftedRep的种类是Int#

TYPE IntRep

现在,我们可以回到为什么ghci> :set -XMagicHash ghci> import GHC.Prim ghci> import GHC.Types ghci> import Data.Kind ghci> :kind (Int :: TYPE 'LiftedRep) (Int :: TYPE 'LiftedRep) :: * ghci> :kind Int# Int# :: TYPE 'IntRep 具有如此奇怪的签名的原因。问题是,我们希望能够在 all 函数中使用undefined,既可以返回类型undefined类型的函数,又可以返回类型{ {1}}(换句话说:Type类型)或返回另一个未提升类型的函数。否则,我们将需要TYPE IntRep的多个不同版本,这很烦人。

解决方案是使Int# 高度多态。签名说:对于任何可能的内存表示形式(undefined)和任何值具有该表示形式的 type undefined都会计算该类型的成员。