基本用法

启用 GADTs 扩展后,除常规数据声明外,还可以声明广义代数数据类型,如下所示:

data DataType a where
    Constr1 :: Int -> a -> Foo a -> DataType a
    Constr2 :: Show a => a -> DataType a
    Constr3 :: DataType Int

GADT 声明明确列出了数据类型具有的所有构造函数的类型。与常规数据类型声明不同,构造函数的类型可以是任何 N-ary(包括 nullary)函数,最终会导致应用于某些参数的数据类型。

在这种情况下,我们宣称 DataType 类型有三个构造函数:Constr1Constr2Constr3

Constr1 构造函数与使用常规数据声明声明的构造函数没有什么不同:data DataType a = Constr1 Int a (Foo a) | ...

但是 Constr2 要求 a 有一个 Show 的实例,因此在使用构造函数时,实例需要存在。另一方面,当模式匹配时,aShow 的一个实例的事实进入范围,所以你可以写:

foo::DataType a -> String
foo val = case val of
    Constr2 x -> show x
    ...

请注意,Show a 约束不会出现在函数的类型中,并且仅在 -> 右侧的代码中可见。

Constr3 的类型为 DataType Int,这意味着只要 DataType a 的值为 Constr3,就知道 a ~ Int。此信息也可以通过模式匹配来恢复。