6

I'm looking at the pipes library source code and for instance in the Core module I don't understand why the author is all over the place using the pattern of defining functions like that:

runEffect = go
  where
    go p = ...

Or:

pull = go
  where
    go a' = ...

Or:

reflect = go
  where
    go p = ...

Is this some trick to enable some optimizations? I find it ugly, if it's some optimization trick I really wish the compiler could do it without things like that. But maybe there's another reason?

duplode
  • 33,731
  • 7
  • 79
  • 150
Emmanuel Touzery
  • 9,008
  • 3
  • 65
  • 81

1 Answers1

7

GHC will only inline non-recursive functions, and only when they are "fully applied" from a syntactic point of view (i.e. at the call site they are applied to the number of arguments that appear in the left hand side in the definition).

In the examples you posted there are no arguments, however the definitions are likely recursive and wouldn't be inlined. Doing this transformation probably allows the definitions to be inlined and specialized (for the concrete types of m etc.) at the call site.

Is this some trick to enable some optimizations? I find it ugly, if it's some optimization trick I really wish the compiler could do it without things like that.

Yeah it's super lame.

jberryman
  • 16,334
  • 5
  • 42
  • 83
  • 2
    `pipes` is a small library with composable abstractions that can be used to build lots of complex logics, so it's natural that heavy optimization pays off at the core library level. It also has nontrivial rewrite rules. In contrast, in normal production code we don't need to concern ourselves with manual worker-wrappers. GHC usually does that fine. – András Kovács Jul 01 '15 at 18:51
  • 1
    Thank you. Thanks to your keywords "fully applied" I've now stumbled upon this: http://stackoverflow.com/questions/11690146/why-does-ghc-consider-the-lhs-syntactically-when-inlining which I hope will share a little more light on the issue for me. It is quite disappointing when you see all the other magic that haskell can achieve. – Emmanuel Touzery Jul 01 '15 at 19:14
  • if it that easy to "inline" a recursive function, why is not GHC doing this trick itself ? – mb14 Jul 01 '15 at 19:20
  • 1
    btw if it's the number of arguments that appear on the LHS that matter, could you write `runEffect = \p -> ...` and get the same performance characteristics? I think that would look much less ugly. – Emmanuel Touzery Jul 01 '15 at 19:33
  • 1
    @EmmanuelTouzery " could you write `runEffect = \p -> ...`" yes that is commonly done for non-recursive functions. – jberryman Jul 01 '15 at 20:14
  • Just to make sure I understand: GHC can inline recursion? even with knowing it's beyond amazing, I would have never expected it to. If yes that's extremely impressive. – MasterMastic Jul 02 '15 at 15:57
  • @mb14 `-fstatic-argument-transformation` does this, but I don't think it's on by default in `-O2` (see [discussion](https://ghc.haskell.org/trac/ghc/ticket/9374)) @MasterMastic no, I don't think it will in general. There are some optimisations in addition to `-fstatic-argument-transformation` that will transform recursive functions into non-recursive ones for you though – jberryman Jul 02 '15 at 17:17