键入同义词系列
类型同义词族只是类型级函数:它们将参数类型与结果类型相关联。它们有三种不同的类型。
封闭式同义词系列
这些工作与普通的值级 Haskell 函数非常相似:你指定了一些子句,将某些类型映射到其他类型:
{-# LANGUAGE TypeFamilies #-}
type family Vanquisher a where
Vanquisher Rock = Paper
Vanquisher Paper = Scissors
Vanquisher Scissors = Rock
data Rock=Rock; data Paper=Paper; data Scissors=Scissors
打开类型同义词系列
这些工作更像是类型类实例:任何人都可以在其他模块中添加更多子句。
type family DoubledSize w
type instance DoubledSize Word16 = Word32
type instance DoubledSize Word32 = Word64
-- Other instances might appear in other modules, but two instances cannot overlap
-- in a way that would produce different results.
类关联类型同义词
开放式家庭也可以与实际类相结合。这通常是当,例如与相关联的数据的家庭 ,一些类的方法需要额外的辅助对象,而这些辅助对象可以是不同的实例不同,但可能也共享。一个很好的例子是 VectorSpace
类 :
class VectorSpace v where
type Scalar v :: *
(*^) :: Scalar v -> v -> v
instance VectorSpace Double where
type Scalar Double = Double
μ *^ n = μ * n
instance VectorSpace (Double,Double) where
type Scalar (Double,Double) = Double
μ *^ (n,m) = (μ*n, μ*m)
instance VectorSpace (Complex Double) where
type Scalar (Complex Double) = Complex Double
μ *^ n = μ*n
注意在前两个实例中,Scalar
的实现是一样的。对于相关的数据族,这是不可能的:数据族是单射的 ,类型同义词族不是。
虽然非注入性开辟了如上所述的一些可能性,但它也使类型推理更加困难。例如,以下内容不会进行类型检查:
class Foo a where
type Bar a :: *
bar::a -> Bar a
instance Foo Int where
type Bar Int = String
bar = show
instance Foo Double where
type Bar Double = Bool
bar = (>0)
main = putStrLn (bar 1)
在这种情况下,编译器无法知道要使用的实例,因为 bar
的参数本身只是一个多态的 Num
文字。并且类型函数 Bar
不能在反方向中解析,正是因为它不是单射 † 而且因此不可逆(可能有多个类型与 Bar a = String
)。
† 由于只有这两种情况下,它是真正射,但是编译器不知道有人不增加更多的实例以后,从而打破行为。