`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 =>
,它在内部实现为函数。
如果您正在寻找类似 javascript 的 undefined
或 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
是另一种。它是像LiftedRep
和IntRep
这样的类型。这些类型没有任何值,它们仅存在于express things at the type level,我们将看到。
有一个名为TYPE
的超级神奇类型级实体,当使用RuntimeRep
类型的类型(即描述内存中表示的类型)参数化时,它返回其值的类型类型有那个代表。例如,Type
是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
现在我们可以回到为什么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
(和 Bool
和 Char
...)的类型是 TYPE 'LiftedRep
。 TYPE 'LiftedRep
类型通常以Type
(或*
)出现在类型签名中。 GHC 用户指南有更多信息:downloads.haskell.org/~ghc/latest/docs/html/users_guide/…以上是关于`undefined` 的类型签名在 Haskell 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章