下面是我在学习Haskell时遇到的问题的提炼版本:
data A = A
data B = B
data Test = TestA A
| TestB B
test :: (a -> a) -> (Test -> Test)
test op t =
case t of
TestA a -> TestA $ op a
TestB b -> TestB $ op b
testA = test id (TestA A)
testB = test id (TestB B)
尝试编译它会出现以下错误:
无法将预期类型'B'与实际类型'a'匹配'a'是由test ::(a - > a)的类型签名绑定的刚性类型变量 - >测试 - >测试
这是怎么回事?我认为当我传入一个多态函数时,我应该能够将它应用于不同具体类型的值 .
1 回答
这里的基本问题是Haskell如何根据类型签名中的自由变量推断量化 . 鉴于以下类型签名......
...类型变量
a
未绑定 . Haskell自动将未绑定的类型变量转换为通用量化约束,因此上述类型实际上解释如下:现在你得到的错误可能会更有意义 - 类型变量
a
只能在每次调用test
时使用一种类型统一,这由调用者决定 . 因此,(a -> a)
函数可以是String -> String
或Int -> Int
或任何其他类型,但它永远不能是一个适用于A
和B
的函数 .但是,显然,当您编写该类型签名时,您有不同的意图 . 您希望
(a -> a)
函数是类型签名,如id
的函数:一个真正适用于任何值的函数,而不是某些特定函数a
的某个特定函数 . 要指定它,必须使forall
显式,以便编译器准确知道应该如何量化该类型变量:但是,上述类型实际上在标准Haskell中无效 . 但是,GHC支持使用
Rank2Types
或RankNTypes
扩展,它允许“更高等级”的多态性,如上面的类型签名 .