我试图了解如何推理部分应用方法链的类型。
我不明白为什么:
:t (+)(+2)
是(a->a)->a->a
或原因:
:t (+)(+)
是(a->a->a)->a->a->a
我的意思是第一个例子
我不明白,当查看(+)
时,我是否需要查看它需要什么a->a->a
或它之前是什么方法(+2)
(需要一个a
)。
在第二个示例中,我知道第一个(+)
需要a->a->a
,但是一旦我完成第一个方法,为什么第二个又需要相同的参数?
答案 0 :(得分:3)
您实际上稍微误报了该类型。那里有一些非常重要的类型类约束:
(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> a -> a
那么这是哪里来的呢?实际上很简单。首先,(+)
的类型签名是
(+) :: Num a => a -> a -> a
或者,重写以使显示的内容清晰明了:
(+) :: Num a => a -> (a -> a)
同时,(+2)
的类型(正是完全执行部分应用程序的结果)是
(+2) :: Num a => a -> a
现在,当您执行(+)(+2)
时,您正在做的是(部分)将(+)
函数应用于函数(+2)
。也就是说,我们将(+2)
视为(+)
的第一个参数。为了使其正常工作,其类型-Num a => a -> a
-必须是Num
的实例。因此,这就是为什么我们还有一个类型约束,即a -> a
必须是Num
的实例的原因。 (默认情况下永远不会这样,但是您可以为数字函数定义自己的实例-通常,它将两个函数都应用于输入并添加结果。)
因此,让我们将(+)
的类型签名专门化到将其应用于函数(a -> a)
的情况(正如我刚才所说的,它本身必须是Num
的实例,以及a
本身)。我们得到:
(+) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a) -> (a -> a)
或带有粗略的显示:
(+) :: (Num a, Num (a -> a)) => (a -> a) -> ((a -> a) -> (a -> a))
也就是说,它接受一个a -> a
函数,并返回类型(a -> a) -> (a -> a)
的“高阶函数”。因此,当我们将其应用于(+2)
时,我们得到的正是这样一个高阶函数:
(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a)
这正是报告的内容,因为最后一对括号是不必要的。 (这是由于再次出现。)
第二种情况完全类似,只是要应用的功能是a -> a -> a
而不是a -> a
。