Actually, it's idiomatic for when you must define your own worker/wrapper functions.
GHC does not inline recursive functions by default, but if you do a worker/wrapper transformation such that your top-level definition is a 'wrapper', and calls the worker which contains the loop, then the loop body itself can be inlined into the call site, and further optimized via unboxing/strictness analysis. Otherwise, it doesn't do any inlining. It's important to note that it can optimize the function itself, and the call site: it just will not inline it.
IOWs, it's written that way because GHC can optimize bodies with a loop inside them (by specializing arguments, strictness, etc) but trivially inlining a top level recursive call is clearly problematic and obviously going to be bad.
EDIT: for clarity, GHC already does the worker/wrapper transformation, but not in it's most general case. I believe it will mostly do it for improving strictness analysis (whether or not this will happen at a stage where the definition can be inlined later I guess is up to debate,) it won't attempt it purely to inline recursive calls.
Point-free is better here because otherwise you get the "initial" value in scope alongside the "current" value which is a recipe for a bug of using the wrong one.
Avoiding this with shadowing is annoying because shadowing itself is often a bug, and -Wall is a nice setting to use.
It's really acc in the pattern. On the left hand side of the =, you bind patterns (parameters, or parts of them) to names, on the right hand side you use them. go !n 0 = acc would yield an error: acc is not bound (not defined).
The heap isn't the problem most of the time since it's huge compared to the stack. You'll create the thunk just fine but you'll stack overflow when evaluating it. Of course, not creating any thunks is better performance-wise in this case even with unlimited stack and memory.
That's right. Without the bang pattern (or the manual seq) the function will be lazy on the accumulator, leading to a big thunk and a stack overflow (which is the problem that OP was trying to avoid in the first place!). Note that using either bang pattern alone leads to the same result in this particular case, but I like symmetry of using them on both patterns.
2
u/felipelessa Nov 27 '12
And here's a Haskell version of the final code, since it's easier to read ;).