种类清单究竟如何运作?

时间:2013-11-26 21:51:55

标签: haskell data-kinds

我最近在vinyl阅读,它使用了奇怪的“种类列表”类型。在阅读了关于种类和乙烯基的一些内容之后,我对它们有了一些直观的了解,并且我已经能够将它们一起破解了

{-# LANGUAGE DataKinds,
             TypeOperators,
             FlexibleInstances,
             FlexibleContexts,
             KindSignatures,
             GADTs #-}
module Main where

-- from the data kinds page, with HCons replaced with :+:
data HList :: [*] -> * where
  HNil :: HList '[]
  (:+:) :: a -> HList t -> HList (a ': t)

infixr 8 :+:


instance Show (HList '[]) where
  show _ = "[]"
instance (Show a, Show (HList t)) => Show (HList (a ': t)) where
  show (x :+: xs) = show x ++ " : " ++  show xs

class ISum a where
  isum :: Integral t => a -> t

instance ISum (HList '[]) where
  isum _ = 0


instance (Integral a, ISum (HList t)) => ISum (HList (a ': t)) where
  isum (x :+: xs) = fromIntegral x + isum xs

-- explicit type signatures just to check if I got them right
alist :: HList '[Integer]
alist = (3::Integer) :+: HNil

blist :: HList '[Integer,Int]
blist =  (3::Integer) :+: (3::Int) :+: HNil

main :: IO ()
main = do
  print alist
  print (isum alist :: Int)
  print blist
  print (isum blist :: Integer)

:i HList收益

data HList $a where
  HNil :: HList ('[] *)
  (:+:) :: a -> (HList t) -> HList ((':) * a t)
    -- Defined at /tmp/test.hs:10:6
instance Show (HList ('[] *)) -- Defined at /tmp/test.hs:17:10
instance (Show a, Show (HList t)) => Show (HList ((':) * a t))
  -- Defined at /tmp/test.hs:19:10
instance ISum (HList ('[] *)) -- Defined at /tmp/test.hs:25:10
instance (Integral a, ISum (HList t)) => ISum (HList ((':) * a t))
  -- Defined at /tmp/test.hs:29:10
*Main> :i HList
data HList $a where
  HNil :: HList ('[] *)
  (:+:) :: a -> (HList t) -> HList ((':) * a t)
    -- Defined at /tmp/test.hs:10:6
instance Show (HList ('[] *)) -- Defined at /tmp/test.hs:17:10
instance (Show a, Show (HList t)) => Show (HList ((':) * a t))
  -- Defined at /tmp/test.hs:19:10
instance ISum (HList ('[] *)) -- Defined at /tmp/test.hs:25:10
instance (Integral a, ISum (HList t)) => ISum (HList ((':) * a t))
  -- Defined at /tmp/test.hs:29:10

我从中收集'[]'[] *的糖和x ': y的{​​{1}}糖。那是什么?在那里做什么?它是那种列表元素吗?另外,这究竟是什么样的清单呢?它是否内置于语言中?

2 个答案:

答案 0 :(得分:7)

*是......不幸。这是GHC针对多边形数据类型的漂亮打印机的结果。它导致语法上无效的东西,但确实传达了一些有用的信息。

当GHC漂亮地打印出具有多态类型的类型时,它会在类型构造函数之后打印每种多态类型变量的类型。为了。所以如果你有一个声明如下:

data Foo (x :: k) y (z :: k2) = Foo y

GHC会将Foo(数据构造函数)的类型打印为y -> Foo k k1 x y z。如果你有一些用处,那就是那种类型变量之一,比如..

foo :: a -> Int -> Foo a Int 5 -- Data Kind promoted Nat

foo "hello" 0的类型将打印为Foo * Nat String Int 5。是的,这太可怕了。但如果你知道发生了什么,至少你可以阅读它。

'[]内容是DataKinds扩展程序的一部分。它允许将类型提升为种类,并且该类型的构造函数成为类型构造函数。这些提升类型没有有效值,甚至没有undefined,因为它们的种类与*不兼容,-- foo.hs {-# LANGUAGE DataKinds, PolyKinds #-} data Foo (x :: k) y (z :: k1) = Foo y 是所有可以使用它们的值的类型。所以他们只能出现在没有那种价值的地方。有关详细信息,请参阅http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/kind-polymorphism-and-promotion.html

修改

你的评论提出了关于ghci工作方式的一些观点。

GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :l foo
[1 of 1] Compiling Main             ( foo.hs, interpreted )
Ok, modules loaded: Main.
*Main> :t Foo
Foo :: y -> Foo * * x y z
*Main> :set -XPolyKinds
*Main> :t Foo
Foo :: y -> Foo k k1 x y z

在ghci中加载文件时,不会以交互方式激活文件中使用的扩展名。

PolyKinds

所以,是的。必须在ghci中启用foo扩展名才能将其默认为类型中的多态种类。我也尝试在文件中定义我的*Main> :set -XDataKinds *Main> let foo :: a -> Int -> Foo a Int 5 ; foo = undefined *Main> :t foo "hello" 0 foo "hello" 0 :: Foo * GHC.TypeLits.Nat [Char] Int 5 *Main> :m + GHC.TypeLits *Main GHC.TypeLits> :t foo "hello" 0 foo "hello" 0 :: Foo * Nat [Char] Int 5 函数,但确实崩溃了这个版本的ghc。哎呦。我认为现在已经修复了,但我想检查ghc trac会很好。在任何情况下,我都可以交互式地定义它并且工作正常。

Nat

好的,好吧,我忘记了显示undefined不合格需要导入。由于我只是在演示类型打印,所以我并不关心实现,所以PolyKinds已经足够了。

但我保证,所做的一切的工作方式如何。我刚刚省略了有关需要哪些扩展程序的详细信息,特别是DataKindsPolyKinds。我认为既然你在代码中使用了那些,你就会理解它们。以下是{{1}}扩展程序的文档:http://www.haskell.org/ghc/docs/7.6.3/html/users_guide/kind-polymorphism.html

答案 1 :(得分:0)

这是由于一些关于印刷的不幸实施。种类可以被认为是“类型的类型”。请注意以下事项:

>:t []
[] :: [a]
>:k '[]
'[] :: [*]

就像[a]表示“适用于所有类型a,[a]”,[*]表示“适用于所有种类 *,{{1 “}。但是,使用种类可以做的推理量比使用类型要小得多。例如,[*]表示两个a -> a的类型相同,但a表示* -> *都可以是任何类型(可以将其视为*类型* -> *“提升”到类型级别。但是没有办法将类型a -> b提升到类型级别。这意味着a -> a[a]并不十分相似。 [*]更接近[*]之类的内容。更简洁,但不太准确,你可以说没有办法区分“多态”类型,因为没有“类型变量”这样的东西。 (旁注:[forall a . a]启用了文档所谓的“多态种类”,但它仍然没有给你真正的多态性)

所以当你写-XPolyKinds(这实际上意味着HList :: [*] -> *)时,你指的是第一个类型参数的种类应该是HList (k :: [*]) :: *,而'值'表示那种类型[*]可以[*]'[]* ': '[]等。

现在问题。当打印类型受限制的事物时,如* ': * ': '[]的第一个类型参数,它将尝试包含所有类型的信息。无论出于何种原因,而不是写

HNil

这实际上表明HNil :: HList ('[] :: '[*]) ^ data ^ type ^ kind 指的是*的类型,它以您目睹过的令人难以置信的格式打印内容。有必要提供这些信息,因为列表中“存储”的东西不一定是开放式的('[]的名称)。你可以有类似的东西:

*

这是非常愚蠢但仍然有效!

我认为这种错误原因有两个原因。首先,如果事物是​​以我所述的格式打印的,那么某些数据类型或类的data Letter = A | B -- | C .. etc data LetterProxy p data HList (k :: [Letter]) where HNil :: HList '[] (:+:) :: LetterProxy (a :: Letter) -> HList t -> HList (a ': t) 结果将非常长并且非常难以理解。其次,种类是非常新的(仅在7.4左右?)所以没有花费大量时间来决定如何打印kinded事物,因为还没有人确定应该/将如何工作。