一个好处是数据族是单射的,不像类型族。
如果你有
type family TF a
data family DF a
那么你知道DF a ~ DF b 意味着a ~ b,而使用TF,你不会——对于任何a,你可以确定DF a 是一个全新的类型(就像[a]是与 [b] 不同的类型,当然 a ~ b 除外),而类型族可以将多个输入类型映射到同一个现有类型。
第二个是数据族可以部分应用,就像任何其他类型构造函数一样,而类型族不能。
这不是一个特别真实的例子,但例如,你可以这样做:
data instance DF Int = DInt Int
data instance DF String = DString String
class C t where
foo :: t Int -> t String
instance C DF where -- notice we are using DF without an argument
-- notice also that you can write instances for data families at all,
-- unlike type families
foo (DInt i) = DString (show i)
基本上,DF 和 DF a 本身就是实际的、一流的、合法的类型,就像您使用 data 声明的任何其他类型一样。 TF a 只是一个计算类型的中间形式。
但我想当我想了解数据系列并阅读类似内容时,所有这些都不是很有启发性,或者至少不适合我。
这是我遵循的经验法则。每当您发现自己重复具有类型族的模式时,并且对于每个输入类型,您声明一个新的data 类型以供类型族映射到,最好去掉中间人并使用数据族。
来自vector 库的真实示例。 vector 有几种不同的向量:装箱向量、未装箱向量、原始向量、可存储向量。对于每个Vector 类型,都有一个对应的可变MVector 类型(普通向量是不可变的)。所以它看起来像这样:
type family Mutable v :: * -> * -> * -- the result type has two type parameters
module Data.Vector{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector
module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector
[etc.]
现在,我宁愿拥有:
data family Mutable v :: * -> * -> *
module Data.Vector{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector
module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector
[etc.]
它编码了对于每个Vector 类型只有一个Mutable Vector 类型的不变量,并且它们之间存在一一对应的关系。 Vector 的可变版本始终称为Mutable Vector:这是它的名字,没有别的名字。如果你有一个Mutable Vector,你可以获得对应的不可变Vector 的类型,因为它作为类型参数就在那里。使用type family Mutable,一旦你将它应用于一个参数,它就会评估为一个未指定的结果类型(大概称为MVector,但你不知道),并且你无法向后映射。