haskell多态函数求值错误
以下代码未编译:haskell多态函数求值错误,haskell,polymorphism,Haskell,Polymorphism,以下代码未编译: foo :: Num a => (a -> a) -> Either Integer Double -> Either Integer Double foo f x = case x of Left i -> Left $ f i Right d -> Right $ f 并给出以下错误: Couldn't match type `Integer' with `Double' Expected type: Either Integ
foo :: Num a => (a -> a) -> Either Integer Double -> Either Integer Double
foo f x = case x of
Left i -> Left $ f i
Right d -> Right $ f
并给出以下错误:
Couldn't match type `Integer' with `Double'
Expected type: Either Integer Double
Actual type: Either Integer a
In the expression: Right $ f d
In a case alternative: Right d -> Right $ f d
这是的后续问题,问题通过使用RankNTypes解决:
(forall a. Num a => a -> a)
但答案什么也没说。我想知道:
- 这个错误的根本原因是什么?
最终的结果只会是一个案例分支,f不会同时被键入两个类型,只要
,就应该检查f::Num a=>(a->a)
的类型,Integer->Integer或Double->Double都应该工作,有人能解释一下为什么会导致错误吗f
- 有没有其他方法来修复错误?为什么RankNTypes会修复错误?这对我来说就像我前几天遇到的单态限制错误一样,但是启用它并不能帮助我解决这个问题,显式类型注释也不起作用
a
,它是Num
的一个实例,并且对于该特定类型有一个类型为a->a
的函数。具体来说,只与整数
一起使用的函数适合这里。当然,这样的函数不适合您的任务,因此编译器理所当然地拒绝您给定类型签名的实现。另一方面,第二个类型签名表示您需要一个类型为a->a
的函数,该函数适用于Num
的所有实例。与前者相比,这种类型受到很大限制
作为一种解决方法,您可以要求函数提供两次:
foo :: (a -> a) -> (b -> b) -> Either a b -> Either a b
foo f g = either (Left . f) (Right . g)
这是一个范围的问题,真的 在原始版本中,
foo
的每个实例化都有一个新的类型变量
foo :: forall a. Num a => (a -> a) -> Either Integer Double -> Either Integer Double
a
对于foo
的每个实例化的整个主体必须相同,并且只涉及一个Num
实例。而在rank-2多态版本中,f
参数的每个实例化都有一个新的类型变量
foo :: (forall a. Num a => a -> a) -> Either Integer Double -> Either Integer Double
您所说的实例数量与实例数量一样多。根本原因是,在您最初的定义中,
a
过于笼统。
考虑:
foo :: Num a => (a -> a) -> Either Integer Double -> Either Integer Double
foo f x = case x of
Left i -> Left $ f i
此时,类型检查器遇到了问题,因为Left$fi
的类型必须是双整数
,因此表达式fi
必须是整数
。但是您说过调用方可以传递任何将数值类型映射到自身的函数。例如,您的类型签名允许传递Double->Double
函数。显然,这样的函数可能永远不会产生整数
,因此f
的应用程序在这里没有很好的类型化
OTOH,如果您将解决方案用于排名更高的类型,您将无法传递任何适用于特定类型的函数-仅适用于所有数值类型的函数。例如,您可以传递否定
,但不能传递((1::Integer)+
)。这绝对有道理,因为在另一种情况下,您也将相同的函数应用于Double
值
因此,为了回答第二个问题,根据您的代码,排名较高的类型解决方案是正确的。显然,如果要将其应用于整数
和双精度
,则只能传递否定
之类的函数
底线:与
f :: (a -> a) -> b
您可以传递函数,如id
,tail
,reverse
,((1::Int)+
)。与
f :: (forall a. a -> a) -> b
您只能为所有a传递带有确切类型签名的函数。a->a(模式变量重命名),例如
id
,但是上面提到的其他变量都没有。想象一下它会进行类型检查。你能打电话给foo(+1.5)(左5)?若否,原因为何?如果是,结果会是什么?除了类型问题,您可以编写foo f=one(Left.f)(Right.f)
。