0

I just learned about pointfree style in Haskell and how it can help tidy up the code and make it easier to read. But sometimes they can make the code a bit too terse.

So, when I should I always use pointfree style and at what scenarios should I absolutely avoid pointfree style in Haskell?

  • It's a matter of taste and style which can be refined by reading and writing code. Also, this has been asked many times before. – pdexter Jul 21 '16 at 13:07
  • 4
    No clear rule here. For what it's worth, I use simple common patterns like chains `f . g . h` but avoid "smart" stuff like `(.) . (. f) . g`. I sometimes abuse `f &&& g` and `f *** g`, and maybe some `curry/uncurry`, but I try to prevent myself to go too far in that realm. When in doubt, stick to pointful. – chi Jul 21 '16 at 13:35
  • There are similar questions already asked and answered here (like http://stackoverflow.com/questions/8681512/what-is-a-general-scheme-for-writing-a-function-in-pointfree-style?rq=1), but as written I think this one cannot be answered. It is too broad and opinion based. – jkeuhlen Jul 21 '16 at 14:17

2 Answers2

6

As already commented, it's a matter of taste and there will always be edge cases where both styles are equally suited (or, indeed, a partially-pointed version is best). However, there are some cases where it's clear enough:

  • If a pointed expression can be η-reduced just like that, it's usually a good idea to do it.

    f x = g (h x)
    

    should better be written

    f = g . h
    
  • If you want to memoise some computation before accepting some function parameters, you must keep these parameters out of the scope. For instance,

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

    isn't optimal performance-wise, because the coefficients will need to be recomputed for every x value received. If you make it point-free:

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

    this problem doesn't arise. (Perhaps GHC will in some cases figure this out by itself, but I wouldn't rely on it.)

    Often though, it is better to make it pointed nevertheless, just not with an x in the same scope as a and b but bound by a dedicated lambda:

    linRegression :: [(Double, Double)] -> Double -> Double
    linRegression ps = \x -> a * x + b
     where a, b = ...
    
  • If the point-free version is actually longer than the pointed version, I wouldn't use it. If you need to introduce tricks to get it point-free like flip and the Monad (a->) instance and this doesn't even make it shorter, then it will almost certainly be less readable than the pointed version.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • I think it's actually pretty hard to know what GHC will do, whether you write a manual lambda or not. In this case, I bet you'll get sharing either way, but the optimizer can add or remove sharing in somewhat hard-to-predict ways. – dfeuer Jul 22 '16 at 02:27
  • @dfeuer: are you sure? I'd be surprised if GHC ever _removed_ sharing, at least unless the shared expression inlines to a few opcodes that are clearly neglectable for performance. – leftaroundabout Jul 22 '16 at 07:34
  • Yes, I'm pretty sure. Roman Cheplyaka had some trouble with it doing so in a situation similar to this one in `regex-applicative` and had to disable some optimization to recover it. – dfeuer Jul 22 '16 at 09:56
  • That's troubling! In fact I'd lean to calling it a GHC bug. – leftaroundabout Jul 22 '16 at 10:20
3

My favorite answer comes from Richard Bird's Thinking Functionally with Haskell: pointfree style helps you reason about function composition while a pointed style helps you reason about function application.

If you find that a pointfree style is awkward for writing a particular function then you generally have two options:

  1. Use a pointed style instead. Sometimes you do want to reason about application.
  2. Redesign your function to be compositional in nature.

In my own programs, I've found that (2) often leads to a better design and that this design can then be more clearly expressed using a pointfree style. Pointfree style is not an end goal: it is a means to achieving a more compositional design.

Rein Henrichs
  • 15,437
  • 1
  • 45
  • 55