首先,您缺少定义的一部分: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) 语法是什么?
当你在构造函数前面看到'标记时,表示它们的promoted type-level counterparts。为了语法方便,promoted lists and tuples 也只需要额外的勾号(我们仍然可以为空类型级列表编写'[],为类型级缺点编写':。所有这些都可以通过@987654339 获得@扩展。
除了避免HCons1 的装箱之外,使用带有元组的newtype 声明(而不是带有两个字段的数据声明)还有什么意义?
是的,这是为了确保HList 具有代表性role,这意味着您可以在HLists1 之间进行强制转换。这有点太复杂了,无法仅在一个答案中进行解释,但这里有一个例子说明当我们有事情时事情并没有按照我们想要的那样发展
data instance HList (x ': xs) = HCons x (HList xs)
而不是newtype instance(并且没有模式)。考虑以下newtypes,它们在表示上分别等效于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,因为角色不同。 (更多关于here。)
1 从技术上讲,data family 整体上没有角色:每个instance/newtype 的角色可以不同 - 这里我们只需要HCons具有代表性的案例,因为那是被胁迫的案例。 Check out this Trac ticket.