FlexibleInstances

先看看下面这个简单的例子:

1
2
3
4
5
6
7
8
-- Learning.hs
data Vector a = Vector a a deriving (Show)

class MyClass a where
    myFun :: a -> a

instance MyClass (Vector a) where
    myFun = id

这样的定义看起来是没有问题的,因为不需要任何编译指令就能通过编译了。 我们可以运行看看:

1
2
3
4
5
6
7
8
9
ghci> :l Learning.hs
[1 of 1] Compiling Main             ( Test.hs, interpreted )
Ok, modules loaded: Main.
ghci> myFun (Vector 1 2)
Vector 1 2
ghci> myFun (Vector 1 2) :: Vector Int
Vector 1 2
ghci> myFun (Vector 1 2) :: Vector Double
Vector 1.0 2.0

但如果我们需要为 Vector a 不同的类型参数实现不同的 myFun 的话呢?

1
2
3
4
5
instance MyClass (Vector Int) where
    myFun = undefined

instance MyClass (Vector Double) where
    myFun = undefined

我们再看看编译时会发生什么?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ghci> :r
[1 of 1] Compiling Main             ( Learning.hs, interpreted )

Learning.hs:10:10:
    Illegal instance declaration for MyClass (Vector Int)
          (All instance types must be of the form (T a1 ... an)
           where a1 ... an are *distinct type variables*,
           and each type variable appears at most once in the instance head.
           Use FlexibleInstances if you want to disable this.)
    In the instance declaration for MyClass (Vector Int)
Failed, modules loaded: none.

编译失败!为什么会编译失败呢?

因为通常情况下,我们不能给多态类型(polymorphic type)的特化版本(specialized version)写类型类实例。 在这个例子中,Vector IntVector Double 就是 Vector a 的特化版本。 如果我们需要为这些特化版本写类型类实例的话,我们就需要开启 FlexibleInstances 编译指令来取消这个限制。

TypeSynonymInstances

理解了上面的 FlexibleInstances 后,TypeSynonymInstances 就容易理解了。

如果我需要为 Vector Int 添加一个别名,然后让这个别名成为 MyClass 类型类的实例,我们就会需要用到 TypeSynonymInstances 编译指令了。

1
2
3
4
type VectorInt = Vector Int

instance MyClass VectorInt where
    myFun = undefined

默认情况下,ghc 编译上面的代码时会报错,原因是 Haskell 98 并不支持这种语法。 要让 ghc 成功编译上面的代码,我们就需要开启 TypeSynonymInstances 这个编译指令了。

参考资料