向我解释(.)(.)的含义。

11

我正在学习 Haskell,虽然我很喜欢这门语言,但我发现 Pointfree 风格完全无法阅读。我遇到了下面这个只包含 ASCII 字符的函数。

f = (.)(.)

虽然我了解它的类型签名和它的作用,但我却无法理解为什么它这样做。所以,请问有人能为我写出它的非点释放版本,并且可以逐步回到点释放版本,就像这样:

f g x y = (g x) + y   
f g x = (+) (g x)    
f g = (+) . g    
f = (.) (+)

18
我该告诉他吗? - Dai
1
你尝试过扩展实现 (.) 看看会得到什么结果吗? - Cubic
@ Dai 我相信他可能知道! - DirtyBit
3
"我发现无点风格完全难以理解" - 我和你一样,朋友!除了一条平坦的流水线使用"."之外,如果有更多内容,我就会迷失方向。每个学习 Haskell 的人都会经历尝试写出所有东西都是无点风格的阶段,然后走出来。 - Benjamin Hodgson
1
(.)(.) 是如何形成的? - luqui
3个回答

18

通常而言,(?)(其中?代表任意中缀运算符)和\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 . g . h = (\x -> f (g x)) . h = \y -> (\x -> f (g x)) (h y) = \y -> f (g (h y))
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)

完成。


为什么 (\c d -> c . d) 替换了 a 而不是 b?我以为 Haskell 是从右到左的。 - Erik
2
@Erik \a b -> ... 的缩写是 \a -> (\b -> ...). 因此,当给定一个参数 a 时,它会产生另一个期望参数 b 的函数。因此,a 首先出现。或者换句话说:给定 (\a b -> ...) 1 2a 将是 1b 将是 2。因此,最左边的参数进入最左边的参数。 - sepp2k

5
我们可以通过对组合子定义进行“模式匹配”来反向工作。给定:
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 (.)

所以通过 eta-contraction

f = B B 

因为
a (b c) = B a b c         -- bidirectional equation

根据定义,Haskell的(.)实际上是B组合子(参见BCKW组合子)。
编辑:潜在地,许多组合子可以匹配相同的代码。这就是为什么同一段代码可以有许多可能的组合编码。例如,(ab)(cd) = (ab)(I(cd)) 是一个有效的转换,可能会导致其他组合子定义与之匹配。选择“最合适”的一个是一种艺术(或者是在具有相当高分支因子的搜索空间中进行搜索)。
那是关于向后走的,正如您所要求的那样。但如果您想向前走,我个人更喜欢组合方法,而不是λ符号操作。我甚至会立即写出许多参数,并在最后摆脱多余的参数:
BBabcdefg = B(ab)cdefg = (ab)(cd)efg 

因此,
BBabcd    = B(ab)cd    = (ab)(cd) 

这就是全部。


我认为这个需要更多的背景信息。它看起来几乎像是反过来写的。 - dfeuer
1
@dfeuer 好吧,OP 要求“反向”……但说真的,我写完后意识到这一点,但认为读者足够聪明可以跟得上(或者等价地,从下往上读)。我还假设/记得“eta-contraction” 很容易在谷歌上搜索到。还需要什么背景信息呢? - Will Ness
“在组合器定义中进行'模式匹配'是什么意思?”这真的让我感到困惑。 - dfeuer
1
@dfeuer,我已经编辑好了答案,感谢讨论。 - Will Ness

5
你还可以逐渐向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

结果显示,xz 其实是(分别)二元和一元函数,因此我会使用不同的标识符:
f g x h y = g x (h y)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接