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

How to reason about partially applied method chaining

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

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

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

您实际上有点误报了类型。那里有一些非常重要的类型类约束:

(+)(+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) 的情况(正如我刚才所说,它本身必须是 [=25= 的实例) ],以及 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 函数和 returns 一个 (a -> a) -> (a -> a) 类型的 "higher-order function"。因此,当我们将其应用于 (+2) 时,我们得到的正是这样一个高阶函数:

(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a)

这正是报道的内容,因为最后一对括号是不必要的。 (这是由于再次柯里化。)

第二种情况完全类似,只是您要应用的函数是 a -> a -> a 而不是 a -> a