如何将多态函数应用于具体类型? [英] How to apply polymorphic function to a concrete type?

查看:64
本文介绍了如何将多态函数应用于具体类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是我在学习Haskell时遇到的问题的简化版本:

Below is a distilled version of a problem I encountered while learning 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)

尝试编译它会出现以下错误:

Trying to compile this gives the following error:

无法将预期的类型"B"与实际类型"a"匹配

Couldn't match expected type ‘B’ with actual type ‘a’

"a"是一个刚性类型变量,由测试的类型签名::(a-> a)-> Test-> Test

‘a’ is a rigid type variable bound by the type signature for test :: (a -> a) -> Test -> Test

这是怎么回事?我以为,当我传入一个多态函数时,应该可以将其应用于不同具体类型的值.

What's going on? I thought that when I pass in a polymorphic function, I should be able to apply it to values of different concrete types.

推荐答案

此处的基本问题是Haskell如何根据类型签名中的自由变量来推断量化.给定以下类型签名...

The basic problem here is how Haskell infers quantification from free variables in type signatures. Given the following type signature...

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

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

...the type variable a is unbound. Haskell automatically converts unbound type variables into universal quantification constraints, so the above type is actually interpreted like this:

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

现在,您遇到的错误可能更有意义-每次调用 test 时,类型变量 a 只能与 one 类型统一代码>,这由调用方决定.因此,(a-> a)函数可以是 String->.字符串 Int->Int 或任何其他类型,但是它永远不能是同时在 A B 上都可以使用的函数.

Now the error you are getting might make a little bit more sense—the type variable a can only unify with one type per invocation of test, which is decided by the caller. Therefore, the (a -> a) function could be String -> String or Int -> Int or any other type, but it can never be a function that works on both A and B.

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

Obviously, though, you had a different intent when you wrote that type signature. You wanted the (a -> a) function to be a type signature like the one for id: a function that truly works for any value, not some particular function for some particular choice of a. To specify this, you must make the forall explicit so that the compiler knows precisely how that type variable should be quantified:

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

但是,以上类型实际上在标准的Haskell中无效.但是,GHC通过使用 Rank2Types RankNTypes 扩展来支持 ,该扩展允许较高等级"的多态性,如上面的类型签名.

However, the above type is actually not valid in standard Haskell. It is supported by GHC, though, by using the Rank2Types or RankNTypes extension, which permits "higher rank" polymorphism like the type signature above.

这篇关于如何将多态函数应用于具体类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆