Haskell: Uncurry, Curry, composition

Haskell: Uncurry, Curry, composition

当我输入以下内容时,我无法理解编译器的功能:

(curry . uncurry) (+) 1 2 

根据我的理解,编译器首先使用 uncurry,这意味着会发生错误,因为 uncurry 函数需要这样的输入:

(curry . uncurry) (+) (1,2)

但显然第一个是对的。不过我不明白为什么。

编译器在评估时具体采取了哪些步骤?

另一个主题包含的问题: 为什么

(uncurry . curry) (+) (1,2) 

不起作用?

After my understanding the compiler goes with uncurry.

不,它与 (curry . uncurry)

如果我们评估 (curry . uncurry) 函数,我们会看到这个缩写:

\x -> curry (uncurry x)

所以 (curry . uncurry) (+) 1 2 函数是以下简称:

(\x -> curry (uncurry x)) (+) 1 2

或因此:

(curry (uncurry (+))) 1 2

uncurry :: (a -> b -> c) -> (a, b) -> c and curry :: ((a, b) -> c) -> a -> b -> c从而变换了一个函数。因此,这意味着 uncurry (+) 确实需要一个 2 元组:

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

但现在通过 curry 函数传递此函数:

curry (uncurry (+)) :: Num a => a -> a -> a

所以 curry 函数撤消了 uncurry(+) 函数所做的转换。因此,这意味着 curry (uncurry (+))(+) 相同,因此:

(curry (uncurry (+))) 1 2

相当于:

(+) 1 2

因此这相当于 3

curry . uncurry 并不完全等同于 id 但是,因为它的类型为:

curry . uncurry :: (a -> b -> c) -> a -> b -> c

这意味着它将函数限制为 a -> (b -> c).

类型的函数

您的表达式解析为

(((curry . uncurry) (+)) 1) 2

因此,它构建函数 (curry . uncurry) (+) 并将其应用于 1,然后将生成的函数应用于 2.

因此,我们从(curry . uncurry) (+)开始,也就是curry (uncurry (+))。为简单起见,假设 (+) 实现为

(+) = \x y -> ...

注意上面是柯里化函数,将 xy 作为单独的参数。然后我们有:

curry (uncurry (+))
= { definition + }
curry (uncurry (\x y -> ...))    -- note the separate arguments
= { definition uncurry }
curry (\(x, y) -> ...)           -- only one pair argument
= { definition curry }
(\x y -> ...)                    -- back to two arguments
= { definition + }
(+)

上面,uncurry 将函数 + 转换为接受一对而不是两个参数的函数。此转换被 curry 反转,它将 pair 参数分成两个单独的参数。

确实,curry . uncurry 是二进制柯里化函数的恒等变换,因为它应用了变换及其逆。因此,它对结果或所涉及函数的类型没有影响。

我们得出结论,您的表达式等价于 ((+) 1) 2,计算结果为 3

这个问题得到了很好的回答,但我想添加一个关于 curry . uncurry 的注释。

您可能听说过 SKI-微积分。如果你还没有,这是一个我们使用 3 个组合器的微积分:

Sabc = ac(bc)
Kab  = a
Ia   = a

广为人知的是Turing-full。

此外,I 组合子是多余的。让我们试着用 SK 组合子来写下来。 主要思想是 I 的唯一参数应该作为 K 的第一个参数传递,K 的第二个参数被占用。这正是 S 组合器所做的:如果我们将 K 作为 S 的第一个参数传递,我们将 return 作为 S 的第三个参数:

SKbc = Kc(bc) = c

我们已经证明 (K*ab = b):

K* = SK

因此,我们只需要选择第二个参数:KS 都可以:

I = SKK = SKS

可以看出,组合子I对应id; 组合子K对应const; 和组合器 S 对应于 (<*>) :: (e -> a -> b) -> (e -> a) -> e -> b.

I = SKK对应const <*> const :: a -> a。 由于键入:

,我们的其他结果(I = SKSK* = SK)不在 Haskell 中
GHCi> :t (<*>) const (<*>) {- id -}
(<*>) const (<*>) {- id -}
  :: Applicative f => f (a -> b) -> f (a -> b)

GHCi> :t (const <*>) {- flip const -}
(const <*>) {- flip const -} :: (b -> a) -> b -> b

正如您所看到的,我们的实现充当了他们域中所需的功能,但我们缩小了域范围。

如果我们将 (<*>) const (<*>) 指定给 reader,我们将得到您写下的函数 curry . uncurry - id 的函数。

curry . uncurry 的另一个类似方法是 ($):

f $ x   = f x
($) f x = f x
($) f   = f
($)     = id

您找到的函数非常有趣 - 寻找其他类似的函数可能是一个很好的练习(我不知道是否还有其他值得注意的函数)。