首页 文章

如何将多态函数应用于具体类型?

提问于
浏览
1

下面是我在学习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 回答

  • 1

    这里的基本问题是Haskell如何根据类型签名中的自由变量推断量化 . 鉴于以下类型签名......

    test :: (a -> a) -> (Test -> Test)
    

    ...类型变量 a 未绑定 . Haskell自动将未绑定的类型变量转换为通用量化约束,因此上述类型实际上解释如下:

    test :: forall a. (a -> a) -> (Test -> Test)
    

    现在你得到的错误可能会更有意义 - 类型变量 a 只能在每次调用 test 时使用一种类型统一,这由调用者决定 . 因此, (a -> a) 函数可以是 String -> StringInt -> Int 或任何其他类型,但它永远不能是一个适用于 AB 的函数 .

    但是,显然,当您编写该类型签名时,您有不同的意图 . 您希望 (a -> a) 函数是类型签名,如 id 的函数:一个真正适用于任何值的函数,而不是某些特定函数 a 的某个特定函数 . 要指定它,必须使 forall 显式,以便编译器准确知道应该如何量化该类型变量:

    test :: (forall a. a -> a) -> (Test -> Test)
    

    但是,上述类型实际上在标准Haskell中无效 . 但是,GHC支持使用 Rank2TypesRankNTypes 扩展,它允许“更高等级”的多态性,如上面的类型签名 .

相关问题