键入同义词系列

类型同义词族只是类型级函数:它们将参数类型与结果类型相关联。它们有三种不同的类型。

封闭式同义词系列

这些工作与普通的值级 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)。

由于只有这两种情况下,它真正射,但是编译器不知道有人不增加更多的实例以后,从而打破行为。