Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
haskell多态函数求值错误_Haskell_Polymorphism - Fatal编程技术网

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)
    ,就应该检查
    f
    的类型,Integer->Integer或Double->Double都应该工作,有人能解释一下为什么会导致错误吗

  • 有没有其他方法来修复错误?为什么RankNTypes会修复错误?这对我来说就像我前几天遇到的单态限制错误一样,但是启用它并不能帮助我解决这个问题,显式类型注释也不起作用


从根本上说,这是一个范围界定问题。让我们比较以下类型的草稿:

foo1::Num a=>(a->a)->

foo2::(对于所有a.Num a=>a->a)->

在第一个声明中,编译器希望有一个类型
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)