在 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 在某些情况下会自己解决这个问题,但我不会依赖它。)
尽管如此,但通常情况下,最好还是指出它,只是不要使用与 a
和 b
相同范围内的 x
,而是受专用 lambda 约束:
linRegression :: [(Double, Double)] -> Double -> Double
linRegression ps = \x -> a * x + b
where a, b = ...
如果无点版本实际上比有点版本长,我不会使用它。如果你需要引入一些技巧来使它像 flip
和 Monad (a->)
实例一样无点,而这甚至不会使它更短,那么它几乎肯定会比有点版本的可读性差。
我最喜欢的答案来自 Richard Bird 的 Thinking Functionally with Haskell:pointfree 风格帮助你推理函数 composition 而尖锐的样式可以帮助您推理函数 application.
如果您发现 pointfree 风格不利于编写特定函数,那么您通常有两个选择:
- 改用尖头款式。有时你确实想对应用程序进行推理。
- 重新设计您的函数,使其本质上是组合的。
在我自己的程序中,我发现 (2) 通常会导致更好的设计,然后可以使用 pointfree 样式更清楚地表达这种设计。 Pointfree 风格不是最终目标:它是实现更组合设计的一种手段。
我刚刚在 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 在某些情况下会自己解决这个问题,但我不会依赖它。)
尽管如此,但通常情况下,最好还是指出它,只是不要使用与
a
和b
相同范围内的x
,而是受专用 lambda 约束:linRegression :: [(Double, Double)] -> Double -> Double linRegression ps = \x -> a * x + b where a, b = ...
如果无点版本实际上比有点版本长,我不会使用它。如果你需要引入一些技巧来使它像
flip
和Monad (a->)
实例一样无点,而这甚至不会使它更短,那么它几乎肯定会比有点版本的可读性差。
我最喜欢的答案来自 Richard Bird 的 Thinking Functionally with Haskell:pointfree 风格帮助你推理函数 composition 而尖锐的样式可以帮助您推理函数 application.
如果您发现 pointfree 风格不利于编写特定函数,那么您通常有两个选择:
- 改用尖头款式。有时你确实想对应用程序进行推理。
- 重新设计您的函数,使其本质上是组合的。
在我自己的程序中,我发现 (2) 通常会导致更好的设计,然后可以使用 pointfree 样式更清楚地表达这种设计。 Pointfree 风格不是最终目标:它是实现更组合设计的一种手段。