您对柯里化的基本理解是正确的。具体来说,它是关于转换一个将其参数作为元组的函数,例如
add :: (Int, Int) -> Int
add (x, y) = x + y
进入一个函数,一次只接受一个参数:
add' :: Int -> Int -> Int
add' x y = x + y
该方案允许您将现在柯里化的函数应用于部分应用程序,即,应用它的一些但不是全部的参数。例如我们可以有
succ :: Int -> Int
succ = add' 1
我们应用add'
到它的第一个参数并产生一个仍然期望剩余参数的函数。
逆变换称为 uncurrying,它将一个“一个接一个”接受参数的函数转换为一个“一次”接受其参数作为元组的函数。
这两种转换都可以被高阶函数族捕获。也就是说,对于二元函数有
curry :: ((a, b) -> c) -> (a -> b -> c)
curry f = \x y -> f (x, y)
uncurry :: (a -> b -> c) -> ((a, b) -> c)
uncurry f = \(x, y) -> f x y
对于三元函数有
curry3 :: ((a, b, c) -> d) -> (a -> b -> c -> d)
curry3 f = \x y z -> f (x, y, z)
uncurry3 :: (a -> b -> c -> d) -> ((a, b, c) -> d)
uncurry3 f = \(x, y, z) -> f x y z
等等。
现在让我们看一下您的示例:
2 * (\x y -> x * y) 2 3
在这里,您将文字2
与应用程序的结果相乘,该函数(\x y -> x * y)
将其两个参数x
和y
. 正如你所看到的,这个函数已经“一个接一个”地接受它的参数。因此,它已经咖喱了。因此,如果他们要求编写此表达式的咖喱版本,您的教程中的含义超出了我的范围。我们可以做的是通过让乘法函数“一次性”接受它的参数来编写一个非柯里化(\(x, y) -> x * y)
版本:然后我们得到
2 * (\(x, y) -> x * y) (2, 3)
现在请注意,可以写(\(x, y) -> x * y)
为uncurry (*)
,这将给我们
2 * uncurry (*) (2, 3)
如果我们也 uncurry 的第一个应用程序(或者实际上是应用程序,复数;-))(*)
,我们产生
uncurry (*) (2, uncurry (*) (2, 3))
我怀疑这是否是您教程中练习的意图,但我希望这能让您对 currying 和 uncurrying 有所了解。