在 haskell 中什么时候使用什么时候不使用 pointfree 风格?

when to use and when not to use pointfree style in haskell?

我刚刚在 Haskell 中了解了 pointfree 风格,以及它如何帮助整理代码并使其更易于阅读。但有时他们会使代码过于简洁。

那么,在Haskell中什么时候应该使用pointfree风格,什么情况下绝对避免使用pointfree风格?

正如已经评论过的,这是一个品味问题,总会有两种风格同样适合的极端情况(或者,事实上,部分尖头的版本是最好的)。但是,在某些情况下它已经足够清楚了:

  • 如果一个尖锐的表达式可以像这样减少 η,那么这样做通常是个好主意。

    f x = g (h x)
    

    最好写成

    f = g . h
    
  • 如果你想在接受一些函数参数之前记住一些计算,你必须将这些参数排除在范围之外。例如,

    linRegression :: [(Double, Double)] -> Double -> Double
    linRegression ps x = a * x + b
     where a, b = -- expensive calculation of regression coefficients,
                  -- depending on the `ps`
    

    在性能方面不是最优的,因为需要为接收到的每个 x 值重新计算系数。如果你让它无积分:

    linRegression :: [(Double, Double)] -> Double -> Double
    linRegression ps = (+b) . (a*)
     where a, b = ...
    

    不会出现这个问题。 (也许 GHC 在某些情况下会自己解决这个问题,但我不会依赖它。)

    尽管如此,但通常情况下,最好还是指出它,只是不要使用与 ab 相同范围内的 x,而是受专用 lambda 约束:

    linRegression :: [(Double, Double)] -> Double -> Double
    linRegression ps = \x -> a * x + b
     where a, b = ...
    
  • 如果无点版本实际上比有点版本,我不会使用它。如果你需要引入一些技巧来使它像 flipMonad (a->) 实例一样无点,而这甚至不会使它更短,那么它几乎肯定会比有点版本的可读性差。

我最喜欢的答案来自 Richard Bird 的 Thinking Functionally with Haskell:pointfree 风格帮助你推理函数 composition 而尖锐的样式可以帮助您推理函数 application.

如果您发现 pointfree 风格不利于编写特定函数,那么您通常有两个选择:

  1. 改用尖头款式。有时你确实想对应用程序进行推理。
  2. 重新设计您的函数,使其本质上是组合的。

在我自己的程序中,我发现 (2) 通常会导致更好的设计,然后可以使用 pointfree 样式更清楚地表达这种设计。 Pointfree 风格不是最终目标:它是实现更组合设计的一种手段。