首先,您缺少定义的一部分:data family
声明本身。
data family HList (l :: [*])
data instance HList '[] = HNil
newtype instance HList (x ': xs) = HCons1 (x, HList xs)
这称为 a data family
(在TypeFamilies
扩展名下可用)。
pattern HCons x xs = HCons1 (x, xs)
这是一个双向模式(在PatternSynonyms
扩展下可用)。
我看到的'[]
and语法是什么?(x ': xs)
当你'
在构造函数前面看到标记时,它表示它们提升的类型级对应物。为了语法上的方便,提升列表和元组也只需要额外的勾号(我们仍然可以'[]
为空类型级列表和':
类型级 cons 编写。所有这些都可以通过DataKinds
扩展获得。
除了避免装箱之外,使用newtype
带有元组的声明(而不是带有两个字段的数据声明)还有什么意义HCons1
?
是的,这是为了确保HList
具有代表性的角色,这意味着您可以在HList
s 1之间进行强制。这有点过于复杂,无法仅在一个答案中解释,但这是一个例子,说明当我们有
data instance HList (x ': xs) = HCons x (HList xs)
而不是newtype instance
(并且没有模式)。考虑以下在表示上分别newtype
等同于Int
、Bool
和的 s()
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从技术上讲,a 没有作为一个整体的角色:每个/data family
的角色可以不同- 这里我们只需要案例具有代表性,因为那是被强制的。看看这张 Trac 票。instance
newtype
HCons