为什么函数组合在应用于采用多个参数的函数时有效?

Why does function composition work when applied to functions that take multiple arguments?

我想我在写出这些步骤时理解函数应用程序是如何工作的,但是类型签名算法并没有在我的脑海中加起来。为冗长的前奏道歉(没有双关语)。

举一个具体的例子,这个例子是 Stefan Höck's Idris2 tutorial:

的一个稍微改动的例子
plusTwo : Integer -> Integer
plusTwo = (+2)

twice : (Integer -> Integer) -> Integer -> Integer
twice f n = f (f n)

在 REPL 中:

> twice plusTwo 3
7

> (twice . twice) plusTwo 3
11

我所知道的

写出(twice . twice) plusTwo 3

表达式可以显式括号为

((twice . twice) plusTwo) 3

可以重写为

       ------f-------- -n-
(twice (twice plusTwo)) 3
             |
             V
------f-------- (------f-------- -n-)
(twice plusTwo) ((twice plusTwo)  3 )
                 \------------------/
                          |||
                  plusTwo (plusTwo 3)
                          |||
                           7                 
\-----------------------------------/
              |||
         twice plusTwo 7

似乎类型签名不匹配

下面的函数组合运算符的类型签名表明它采用单参数函数,

(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)

但是 twice 需要两个参数(即 (t -> t) -> t -> t),所以这让我失望。

我想当返回的 lambda 的参数 x 本身是一个函数时,这是唯一可行的方法。能这么简单吗?

        twice        .          twice
((a -> a) -> a -> a) -> ((a -> a) -> a -> a) ->  ?

(---b---- -> --c---) -> (---a---- -> --b---) -> (a -> c)

? = (a -> a) -> a -> a

或者,换句话说,twice . twice 接受一个签名为 (a -> a) -> a 的函数(这里的 aInteger)。


如果上面的内容是正确的,那么我可以找出参与函数具有不同输入参数的函数组合(例如,twice . (+2))。

是的,仅此而已。如果把twice的签名写成

可能会更容易思考
twice :: (Integer -> Integer) -> (Integer -> Integer)

如您所知,这等同于柯里化。这样看来,twice 是一个参数的函数,再次将它与 twice 组合起来似乎非常合理。