如何推理部分应用的方法链

时间:2019-04-03 09:26:44

标签: haskell partial-application

我试图了解如何推理部分应用方法链的类型。 我不明白为什么:
:t (+)(+2)(a->a)->a->a
或原因:
:t (+)(+)(a->a->a)->a->a->a

我的意思是第一个例子 我不明白,当查看(+)时,我是否需要查看它需要什么a->a->a或它之前是什么方法(+2)(需要一个a)。

在第二个示例中,我知道第一个(+)需要a->a->a,但是一旦我完成第一个方法,为什么第二个又需要相同的参数?

1 个答案:

答案 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