当我使用 GADT 语法定义一个新类型时,"普通"代数数据类型和广义代数数据类型之间有什么确切的区别?我认为这与所定义的数据构造函数的类型签名有关,但从未找到确切的定义。
此外,这种差异的后果是什么,为什么要显式启用 GADTs?我读到它们使类型推断变得不可判定,但为什么我们不能将类型构造函数视为具有该类型签名的函数,并将其插入推断算法中呢?
当我使用 GADT 语法定义一个新类型时,"普通"代数数据类型和广义代数数据类型之间有什么确切的区别?我认为这与所定义的数据构造函数的类型签名有关,但从未找到确切的定义。
此外,这种差异的后果是什么,为什么要显式启用 GADTs?我读到它们使类型推断变得不可判定,但为什么我们不能将类型构造函数视为具有该类型签名的函数,并将其插入推断算法中呢?
data NonGADT a where
Agnostic :: a -> NonGADT a
data GADT a where
IntSpecific :: Int -> GADT Int
CharSpecific :: Char -> GADT Char
a -> □ADT a
,因此从类型检查器的角度来看,当对它们进行模式匹配时,编译器需要处理的就是这个签名。extractNonGADT :: NonGADT a -> a
extractNonGADT v = case v of
Agnostic x -> x
extractGADT :: GADT a -> a
extractGADT v = case v of
IntSpecific x -> x
CharSpecific x -> x
区别在于extractGADT
可以利用内部类型实际上被限制为一个的事实,来执行类似以下的操作:
extractGADT' :: GADT a -> a
extractGADT' v = case v of
IntSpecific x -> x + 5
CharSpecific x -> toUpper x
a
在整个定义中是相同的。实际上,从技术上讲,在GHC中它也是相同的,只是代码的某些部分有额外的约束a ~ Int
或a ~ Char
。但是仅仅添加处理这些约束是不够的:这些额外的信息不能逃离作用域(因为它们是错误的),这需要进行额外的检查。区别在于返回类型的多态性。如果完全多态,您可以使用语法,但任何特殊化都需要 GADTs 语法。例如:
Foo :: Int -> Bar a b c
a
、b
和 c
都是不同的类型变量,所以这适用于 GADTSyntax
扩展。但如果你要写其中任何一个,你需要使用 GADTs
。Foo :: Int -> Bar Int b c
Foo :: Int -> Bar a b b
Foo :: Eq a => Int -> Bar a b c
-- (actually, ExistentialQuantification is enough for this last one, but GADTs also works)
x + 5
和toUpper x
的类型? - pgmcr