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

tor*_*tte 4 haskell functional-programming idris

我想我在写出步骤时理解了函数应用程序的工作原理,但类型签名算术并没有在我的脑海中相加。对冗长的前奏表示歉意(没有双关语)。

\n

举一个具体的例子,这个例子是Stefan H\xc3\xb6ck\ 的 Idris2 教程的一个稍微修改过的例子:

\n
plusTwo : Integer -> Integer\nplusTwo = (+2)\n\ntwice : (Integer -> Integer) -> Integer -> Integer\ntwice f n = f (f n)\n
Run Code Online (Sandbox Code Playgroud)\n

在 REPL 中:

\n
> twice plusTwo 3\n7\n\n> (twice . twice) plusTwo 3\n11\n
Run Code Online (Sandbox Code Playgroud)\n

我知道的

\n\n

写出(twice . twice) plusTwo 3

\n

该表达式可以显式地用括号表示为

\n
((twice . twice) plusTwo) 3\n
Run Code Online (Sandbox Code Playgroud)\n

可以重写为

\n
       ------f-------- -n-\n(twice (twice plusTwo)) 3\n             |\n             V\n------f-------- (------f-------- -n-)\n(twice plusTwo) ((twice plusTwo)  3 )\n                 \\------------------/\n                          |||\n                  plusTwo (plusTwo 3)\n                          |||\n                           7                 \n\\-----------------------------------/\n              |||\n         twice plusTwo 7\n
Run Code Online (Sandbox Code Playgroud)\n

看起来类型签名不匹配

\n

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

\n
(.) :: (b -> c) -> (a -> b) -> a -> c\nf . g = \\x -> f (g x)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
        twice        .          twice\n((a -> a) -> a -> a) -> ((a -> a) -> a -> a) ->  ?\n\n(---b---- -> --c---) -> (---a---- -> --b---) -> (a -> c)\n\n? = (a -> a) -> a -> a\n
Run Code Online (Sandbox Code Playgroud)\n

或者,换句话说,twice . twice采用带有签名的函数(a -> a) -> aa这里是Integer)。

\n
\n

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

\n

ama*_*loy 9

是的,仅此而已。twice如果你写下as的签名,可能会更容易思考

twice :: (Integer -> Integer) -> (Integer -> Integer)
Run Code Online (Sandbox Code Playgroud)

如您所知,这相当于柯里化。从这个角度来看,twice是一个参数的函数,并且twice再次组合它似乎是完全明智的。