我对Haskell相对较新,我试图理解HList的定义之一。
data instance HList '[] = HNil
newtype instance HList (x ': xs) = HCons1 (x, HList xs)
pattern HCons x xs = HCons1 (x, xs)
我有几个具体问题:
我看到的
'[]
和(x ': xs)
语法是什么?它看起来几乎像是在可变参数类型参数上进行模式匹配,但我以前从未见过这种语法,也不熟悉 Haskell 中的可变参数类型参数。我猜这是GHC类型系列的一部分,但我在链接页面上没有看到任何关于此的内容,并且在Google中搜索语法相当困难。除了避免
HCons1
装箱之外,使用带有元组的newtype
声明(而不是带有两个字段的data
声明)有什么意义吗?
首先,您缺少定义的一部分:data family
声明本身。
data family HList (l :: [*])
data instance HList '[] = HNil
newtype instance HList (x ': xs) = HCons1 (x, HList xs)
这称为data family
(在TypeFamilies
扩展下可用)。
pattern HCons x xs = HCons1 (x, xs)
这是一个双向模式(在PatternSynonyms
扩展下可用)。
我看到的
'[]
和(x ': xs)
语法是什么?
当您在构造函数前面看到'
标记时,它表示它们提升的类型级对应项。作为语法上的方便,提升的列表和元组也只需要额外的勾号(我们仍然可以为空的类型级列表编写'[]
,为类型级缺点编写':
。所有这些都可以通过DataKinds
扩展获得。
除了避免
HCons1
装箱之外,使用带有元组的newtype
声明(而不是带有两个字段的数据声明)有什么意义吗?
是的,这是为了确保HList
具有代表性角色,这意味着您可以在HList
s 1之间强制。这有点太复杂了,无法仅用答案来解释,但这里有一个例子,说明当我们有
data instance HList (x ': xs) = HCons x (HList xs)
而不是newtype instance
(没有模式)。考虑以下newtype
,它们在代表性上分别等同于Int
、Bool
和()
newtype MyInt = MyInt Int
newtype MyBool = MyBool Bool
newtype MyUnit = MyUnit ()
回想一下,我们可以使用coerce
自动包装或解开这些类型。好吧,我们希望能够做同样的事情,但整个HList
:
ghci> l = (HCons 3 (HCons True (HCons () HNil))) :: HList '[Int, Bool, ()]
ghci> l' = coerce l :: HList '[MyInt, MyBool, MyUnit]
这适用于newtype instance
变体,但由于角色的原因,不适用于data instance
变体。(更多关于这一点 这里.)
1从技术上讲,整个data family
没有角色:每个instance
/newtype
的角色可能不同 - 在这里我们只需要HCons
案例具有代表性,因为这是被胁迫的案例。看看这张Trac票。
'[]
和(x ': xs)
是类型级列表的语法,因为DataKinds
语言扩展允许将类型提升为种类,将构造函数提升为类型;即如果k
是某种类型,那么'[k]
也是一种类型,'[]
是一种类型'[k]
,如果t :: k
和ts :: '[k]
, 然后t ': ts :: '[k]
.一切都被改变了一个。
所以在HList (x ': xs)
中,x
和xs
匹配两种类型:x
匹配一种"正常"类型的*
(例如Int
),并且xs
匹配另一种类型级别列表'[*]
。右侧定义了一个(newtype
)数据类型,该数据类型具有构造函数HCons1
,参数类型为(x, HList xs)
。
例如,我们可以有
HCons1 (1, HCons1 (True, HNil)) :: HList '[Int, Bool]
或者,使用模式同义词:
1 `HCons` True `HCons` HNil :: HList '[Int, Bool]
对于您的第二个问题,我没有得到很好的答案,即为什么它被表示为带有元组的新类型。