`undefined` 的类型签名在 Haskell 中是啥意思?

Posted

技术标签:

【中文标题】`undefined` 的类型签名在 Haskell 中是啥意思?【英文标题】:What does type signature for `undefined` mean in Haskell?`undefined` 的类型签名在 Haskell 中是什么意思? 【发布时间】:2019-05-26 07:24:02 【问题描述】:

我是 Haskell 的初学者,我被 undefined 函数的类型签名吓了一跳。

我期待一些更简单的东西,但我在 Hackage 上找到了这个:

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

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

你能解释一下这个签名是什么意思吗?

谢谢!

【问题讨论】:

首先,undefined 不是一个函数。但如果你还是个初学者,我就假装类型是undefined :: a。剩下的就是高级魔法了。 相关:***.com/questions/49986342/…***.com/questions/35318562/… 我知道我需要修改我的评论:undefined 不是语言(用户)级别的功能。但是,由于类约束HasCallStack =>,它在内部实现为函数。 如果您正在寻找类似 javascriptundefined 或 Java 的 null,您可能想要使用 Data.Maybe。一个很好的介绍是learnyouahaskell.com/modules 【参考方案1】:

对于初学者需要知道的所有方面,签名很简单

undefined :: a

这意味着,与往常一样,a 是 universally quantified 的类型变量(即任何小写字母),也可以明确表示:

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

...或者,我更喜欢这样写

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

量化被推断为超过所有类型,即所有具有种类* 的事物(阅读:“类型”,更准确地说提升类型的种类 –提升意味着它可以是懒惰的重击)。因此,您可以在任何表达式中使用undefined,无论需要什么类型。

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

undefined :: ∀ a. HasCallStack => a

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

标识符 :: 量词约束 => 类型

HasCallStack 是一个约束,这就是它出现在中间的原因。 (更多情况下,约束实际上适用于您已量化的类型变量之一。)

最后,这个RunTimeRep 的东西是关于高度多态性。这个我自己也不是很懂,不过在Why is the undefined function levity-polymorphic when using it with unboxed types?有讨论过

【讨论】:

我真的很想对这个问题进行彻底的回答,只要看看它需要多长时间。这真是一个相当尴尬的局面……【参考方案2】:

这是@leftaroundabout 回答的延续。

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

Haskell 中最重要和最常见的类型是Type,通常在签名中表示为*。它是被“提升”的类型,也就是说,其值可以是 thunk,在评估时可能会发散、抛出错误等......例如,类型 Int 具有类型 Type

还有其他类型,例如Int#没有解除。 Int# 类型的值从不 thunk,它始终是内存中的实际值。

简而言之,值的内存表示由其类型的类型控制

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

有一个名为TYPE 的超级神奇类型级实体,当使用RuntimeRep 类型的类型(即描述内存中表示的类型)参数化时,它返回其值的类型类型有那个代表。例如,TypeTYPE 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

现在我们可以回到为什么undefined 有这么奇怪的签名。问题是,我们希望能够在所有函数中使用undefined,无论是返回类型Type的函数,还是返回类型TYPE IntRep的函数(在换句话说:Int# 类型)或返回另一个未提升类型的函数。否则,我们将需要多个不同版本的undefined,这会很烦人。

解决方案是使undefined levity-polymorphic。签名说:对于任何可能的内存表示 (RuntimeRep) 和任何可能的 type 其值具有该表示,undefined 计算该类型的成员。

【讨论】:

IntRep 中的Int 代表什么,是“int”还是“internal”(或其他)?即Double#是什么类型的,是TYPE 'DoubleRep吗? TYPE 'LiftedRep(Int :: TYPE 'LiftedRep) 中是Int种类吗?如果不是,那是什么?谢谢。 @WillNess IntRep 中的 Int 确实代表“整数”。 Double# 的类型是 TYPE 'DoubleRep,是的,Int(和 BoolChar...)的类型是 TYPE 'LiftedRepTYPE 'LiftedRep 类型通常以Type(或*)出现在类型签名中。 GHC 用户指南有更多信息:downloads.haskell.org/~ghc/latest/docs/html/users_guide/…

以上是关于`undefined` 的类型签名在 Haskell 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

Haskell类型错误

JavaScript中的Undefined / Null 类型相关说明

postman自动生成签名

Null 和 Undefined

null与undefined的区别

JS中Null与Undefined的区别