Haskell 中 `undefined` 的类型签名是什么意思?
What does type signature for `undefined` mean in Haskell?
我是 Haskell 的初学者,我对 undefined
函数的类型签名感到吃惊。
我期待更简单的东西,但我在 Hackage 上发现了这个:
undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r). HasCallStack => a
A special case of error. It is expected that compilers will recognize this and insert error messages which are more appropriate to the context in which undefined
appears.
你能解释一下这个签名是什么意思吗?
谢谢!
对于初学者需要知道的所有问题,签名很简单
undefined :: a
这意味着,与类型变量(即任何小写字母)一样,a
是 universally quantified,也可以明确表示:
{-# LANGUAGE ExplicitForall #-}
undefined :: forall a. a
...或者,我更喜欢这样写
{-# LANGUAGE ExplicitForall, UnicodeSyntax #-}
undefined :: ∀ a. a
量化推断是超越所有类型,即all things with kind *
(读作:“type”,更准确的是the一种提升类型——提升意味着它可以是懒惰的thunk)。因此,您可以在任何表达式中使用 undefined
,无论需要什么类型。
现在,undefined
当然是一个“部分函数”之类的东西,基本上是一个零参数的函数,在任何地方都没有定义。 (FTR,它 不是 函数,根据定义 有 参数 [s]。)
您希望在实际评估时得到 有用的 错误消息,但 GHC 默认情况下不会为所有内容生成调用堆栈(出于性能原因),所以过去的情况是错误信息几乎完全没有用。这就是 HasCallStack
出现的地方:这是一个约束,它本质上告诉上下文某些代码可能会导致 undefined
它应该注意它发生的地方,以便错误消息实际显示它向上。所以,
undefined :: ∀ a. HasCallStack => a
HasCallStack
出现在 ∀ a
之后有点令人困惑——这与 a
没有任何关系,但与 [=16= 的上下文有关] 将会被使用。只是,签名的形式总是
Identifier :: Quantifiers. Constraints => Type
和 HasCallStack
是一个约束条件,这就是它出现在中间的原因。 (更多时候,约束实际上适用于您量化的类型变量之一。)
最后,这个 RunTimeRep
内容是关于 levity 多态性 的。我自己也不是很明白,但是
中对此进行了讨论
这是@leftaroundabout 回答的延续。
在Haskell中,值有类型。但是类型 本身 也有类型。当一个类型充当另一个类型的类型时,我们称它为 "kind".
Haskell 中最重要和最常见的类型是 Type
,通常在签名中表示为 *
。它是 "lifted" 类型的类型,也就是说,其值可以是 thunks,在评估时可以发散、抛出错误等......例如类型 Int
有种类 Type
.
还有其他类型 Int#
未 解除。 Int#
类型的值 永远不会 thunk,它始终是内存中的实际值。
简而言之,值的 in-memory 表示 由其类型的种类 控制。
RuntimeRep
is another kind. It is the kind of types like LiftedRep
and IntRep
. These types don't have any values, they exist merely to express things at the type level,我们将看到。
有一个名为 TYPE
的 super-magical type-level 实体,当使用类型 RuntimeRep
进行参数化时(即使用描述 in-memory 表示)return 是其值具有该表示的类型。例如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
,无论是 return 类型的 Type
函数,还是return 类型 TYPE IntRep
的函数(换句话说:Int#
类型)或 return 另一种未提升类型的函数。否则,我们将需要多个不同版本的 undefined
,这会很烦人。
解决办法就是让undefined
levity-polymorphic。签名说:对于任何可能的 in-memory 表示 (RuntimeRep
) 和任何可能的 type 其值具有该表示,undefined
计算一个成员那种。
我是 Haskell 的初学者,我对 undefined
函数的类型签名感到吃惊。
我期待更简单的东西,但我在 Hackage 上发现了这个:
undefined :: forall (r :: RuntimeRep). forall (a :: TYPE r). HasCallStack => a
A special case of error. It is expected that compilers will recognize this and insert error messages which are more appropriate to the context in which
undefined
appears.
你能解释一下这个签名是什么意思吗?
谢谢!
对于初学者需要知道的所有问题,签名很简单
undefined :: a
这意味着,与类型变量(即任何小写字母)一样,a
是 universally quantified,也可以明确表示:
{-# LANGUAGE ExplicitForall #-}
undefined :: forall a. a
...或者,我更喜欢这样写
{-# LANGUAGE ExplicitForall, UnicodeSyntax #-}
undefined :: ∀ a. a
量化推断是超越所有类型,即all things with kind *
(读作:“type”,更准确的是the一种提升类型——提升意味着它可以是懒惰的thunk)。因此,您可以在任何表达式中使用 undefined
,无论需要什么类型。
现在,undefined
当然是一个“部分函数”之类的东西,基本上是一个零参数的函数,在任何地方都没有定义。 (FTR,它 不是 函数,根据定义 有 参数 [s]。)
您希望在实际评估时得到 有用的 错误消息,但 GHC 默认情况下不会为所有内容生成调用堆栈(出于性能原因),所以过去的情况是错误信息几乎完全没有用。这就是 HasCallStack
出现的地方:这是一个约束,它本质上告诉上下文某些代码可能会导致 undefined
它应该注意它发生的地方,以便错误消息实际显示它向上。所以,
undefined :: ∀ a. HasCallStack => a
HasCallStack
出现在 ∀ a
之后有点令人困惑——这与 a
没有任何关系,但与 [=16= 的上下文有关] 将会被使用。只是,签名的形式总是
Identifier :: Quantifiers. Constraints => Type
和 HasCallStack
是一个约束条件,这就是它出现在中间的原因。 (更多时候,约束实际上适用于您量化的类型变量之一。)
最后,这个 RunTimeRep
内容是关于 levity 多态性 的。我自己也不是很明白,但是
这是@leftaroundabout 回答的延续。
在Haskell中,值有类型。但是类型 本身 也有类型。当一个类型充当另一个类型的类型时,我们称它为 "kind".
Haskell 中最重要和最常见的类型是 Type
,通常在签名中表示为 *
。它是 "lifted" 类型的类型,也就是说,其值可以是 thunks,在评估时可以发散、抛出错误等......例如类型 Int
有种类 Type
.
还有其他类型 Int#
未 解除。 Int#
类型的值 永远不会 thunk,它始终是内存中的实际值。
简而言之,值的 in-memory 表示 由其类型的种类 控制。
RuntimeRep
is another kind. It is the kind of types like LiftedRep
and IntRep
. These types don't have any values, they exist merely to express things at the type level,我们将看到。
有一个名为 TYPE
的 super-magical type-level 实体,当使用类型 RuntimeRep
进行参数化时(即使用描述 in-memory 表示)return 是其值具有该表示的类型。例如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
,无论是 return 类型的 Type
函数,还是return 类型 TYPE IntRep
的函数(换句话说:Int#
类型)或 return 另一种未提升类型的函数。否则,我们将需要多个不同版本的 undefined
,这会很烦人。
解决办法就是让undefined
levity-polymorphic。签名说:对于任何可能的 in-memory 表示 (RuntimeRep
) 和任何可能的 type 其值具有该表示,undefined
计算一个成员那种。