给我解释一下
Explain (.)(.) to me
深入研究 Haskell,当我享受这种语言时,我发现 pointfree 风格完全难以辨认。我遇到了一个只包含这些 ASCII boobies 的函数,如下所示。
f = (.)(.)
虽然我理解它的类型签名和它的作用,但我终究无法理解它为什么这样做。那么有人可以为我写出它的 de-pointfreed 版本,并且可能会像这样逐步回到 pointfree 版本:
f g x y = (g x) + y
f g x = (+) (g x)
f g = (+) . g
f = (.) (+)
通常 (?)
(其中 ?
代表任意中缀运算符)与 \x y -> x ? y
相同。所以我们可以将f
重写为:
f = (\a b -> a . b) (\c d -> c . d)
现在,如果我们将参数应用于函数,我们将得到:
f = (\b -> (\c d -> c . d) . b)
现在 b
只是 f
的参数,因此我们可以将其重写为:
f b = (\c d -> c . d) . b
.
的定义是f . g = \x -> f (g x)
。如果用它的定义替换外部 .
,我们得到:
f b = \x -> (\c d -> c . d) (b x)
同样我们可以把x
变成一个常规参数:
f b x = (\c d -> c . d) (b x)
现在让我们替换另一个.
:
f b x = (\c d y -> c (d y)) (b x)
现在让我们应用参数:
f b x = \d y -> (b x) (d y)
现在让我们再次移动参数:
f b x d y = (b x) (d y)
完成。
我们可以通过"pattern matching"对组合器的定义进行倒推。鉴于
f a b c d = a b (c d)
= (a b) (c d)
我们继续
= B (a b) c d
= B B a b c d -- writing B for (.)
f = B B
因为
a (b c) = B a b c -- bidirectional equation
根据定义。 Haskell 的 (.)
实际上是 B 组合子(参见 BCKW combinators)。
edit: 许多组合器可能会匹配相同的代码。这就是为什么同一段代码有许多可能的组合编码。例如,(ab)(cd) = (ab)(I(cd))
是一个有效的转换,它可能会导致一些其他组合子定义匹配 that。选择 "most appropriate" 一个是一门艺术(或者在搜索 space 中搜索具有较高的分支因子)。
这就是向后,正如你所问的那样。但是,如果您想使用 "forward",就个人而言,我更喜欢组合方法,而不是坐立不安的 lambda 表示法。我什至会直接写很多参数,最后去掉多余的:
BBabcdefg = B(ab)cdefg = (ab)(cd)efg
因此,
BBabcd = B(ab)cd = (ab)(cd)
仅此而已。
您也可以逐渐将参数附加到 f
:
f = ((.) . )
f x = (.) . x
f x y = ((.) . x) y
= (.) (x y)
= ((x y) . )
f x y z = (x y) . z
f x y z t = ((x y) . z) t
= (x y) (z t)
= x y (z t)
= x y $ z t
结果表明 x
和 z
实际上是(分别是二元函数和一元函数),所以我将使用不同的标识符:
f g x h y = g x (h y)
深入研究 Haskell,当我享受这种语言时,我发现 pointfree 风格完全难以辨认。我遇到了一个只包含这些 ASCII boobies 的函数,如下所示。
f = (.)(.)
虽然我理解它的类型签名和它的作用,但我终究无法理解它为什么这样做。那么有人可以为我写出它的 de-pointfreed 版本,并且可能会像这样逐步回到 pointfree 版本:
f g x y = (g x) + y
f g x = (+) (g x)
f g = (+) . g
f = (.) (+)
通常 (?)
(其中 ?
代表任意中缀运算符)与 \x y -> x ? y
相同。所以我们可以将f
重写为:
f = (\a b -> a . b) (\c d -> c . d)
现在,如果我们将参数应用于函数,我们将得到:
f = (\b -> (\c d -> c . d) . b)
现在 b
只是 f
的参数,因此我们可以将其重写为:
f b = (\c d -> c . d) . b
.
的定义是f . g = \x -> f (g x)
。如果用它的定义替换外部 .
,我们得到:
f b = \x -> (\c d -> c . d) (b x)
同样我们可以把x
变成一个常规参数:
f b x = (\c d -> c . d) (b x)
现在让我们替换另一个.
:
f b x = (\c d y -> c (d y)) (b x)
现在让我们应用参数:
f b x = \d y -> (b x) (d y)
现在让我们再次移动参数:
f b x d y = (b x) (d y)
完成。
我们可以通过"pattern matching"对组合器的定义进行倒推。鉴于
f a b c d = a b (c d)
= (a b) (c d)
我们继续
= B (a b) c d
= B B a b c d -- writing B for (.)
f = B B
因为
a (b c) = B a b c -- bidirectional equation
根据定义。 Haskell 的 (.)
实际上是 B 组合子(参见 BCKW combinators)。
edit: 许多组合器可能会匹配相同的代码。这就是为什么同一段代码有许多可能的组合编码。例如,(ab)(cd) = (ab)(I(cd))
是一个有效的转换,它可能会导致一些其他组合子定义匹配 that。选择 "most appropriate" 一个是一门艺术(或者在搜索 space 中搜索具有较高的分支因子)。
这就是向后,正如你所问的那样。但是,如果您想使用 "forward",就个人而言,我更喜欢组合方法,而不是坐立不安的 lambda 表示法。我什至会直接写很多参数,最后去掉多余的:
BBabcdefg = B(ab)cdefg = (ab)(cd)efg
因此,
BBabcd = B(ab)cd = (ab)(cd)
仅此而已。
您也可以逐渐将参数附加到 f
:
f = ((.) . )
f x = (.) . x
f x y = ((.) . x) y
= (.) (x y)
= ((x y) . )
f x y z = (x y) . z
f x y z t = ((x y) . z) t
= (x y) (z t)
= x y (z t)
= x y $ z t
结果表明 x
和 z
实际上是(分别是二元函数和一元函数),所以我将使用不同的标识符:
f g x h y = g x (h y)