首先,您缺少定义的一部分: data family
声明本身。
data family HList (l :: [*])
data instance HList '[] = HNil
newtype instance HList (x ': xs) = HCons1 (x, HList xs)
这被称为
数据家族
(在
TypeFamilies
扩展下可用)。
pattern HCons x xs = HCons1 (x, xs)
这是一个双向模式(可在PatternSynonyms
扩展下使用)。
我看到的'[]
和(x ': xs)
语法是什么意思?
当您在构造函数前面看到'
标记时,它是为了表示它们的提升类型级别对应项。作为一种语法上的便利,提升列表和元组也只需要额外的撇号(我们仍然可以写'[]
表示空的类型级别列表和':
表示类型级别cons)。所有这些都可通过DataKinds
扩展获得。
除了避免HCons1
的装箱之外,使用包含元组的newtype
声明是否有任何意义?
是的,这是为了确保HList
具有可表示角色,这意味着您可以在HList
之间进行强制转换1。这有点复杂,不能仅通过一个答案来解释,但以下是一个示例,说明当我们拥有某些内容时,事情并不如我们所希望的。
data instance HList (x ': xs) = HCons x (HList xs)
不使用newtype instance
(也不使用模式),考虑以下与Int
、Bool
和()
表示上等效的newtype
。
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实例
变体,但不适用于
data实例
,因为涉及到角色问题。(更多信息请参见
此处。)
1 从技术上讲,data family
没有整体的角色:每个 instance
/newtype
的角色可能不同 - 在这里我们只需要 HCons
的情况是可表示的,因为它是被强制转换的那一个。查看此 Trac ticket。
(l :: [*])
是否意味着类型参数l
受到了[*]
类型约束? - lcmylinKindSignatures
,并且可能还需要导入Data.Kind
... 只需按照 GHC 建议的做即可。 :) - Alec