Search code examples
functionhaskellpointfree

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


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?


Solution

  • 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.