
On Tue, Dec 04, 2007 at 07:43:36PM -0800, Ryan Ingram wrote:
On 12/4/07, Stefan O'Rear
wrote: When you see an expression of the form:
f a
you generally want to evaluate a before applying; but if a is _|_, this will only give the correct result if f a = _|_. Merely 'guaranteed to evaluate' misses out on some common cases, for instance ifac:
ifac 0 a = a ifac n a = ifac (n - 1) (a * n)
ifac is guaranteed to either evaluate a, or go into an infinite loop - so it can be found strict, and unboxed. Whereas 'ifac -1 (error "moo")' is an infinite loop, so using a definition based on evaluation misses this case.
By this line: you generally want to evaluate a before applying; but if a is _|_, this will only give the correct result if f a = _|_
I assume you mean that it's generally more efficient to do things that way, because the calling function may have more information about "a" or how it is calculated, so you may be able to optimize better by doing eager evaluation whenever it is correct.
Yes - if we know that a value is needed, eager evaluation is more efficient, because no time need be spent constructing and deconstructing expressions in memory. More significantly, strictness facilitates certain unboxing transformations. Since ifac is strict, (optimized) code will never call it with anything except a concrete number, so we can gainfully specialize it to the case of a pre-evaluated argument; so instead of passing pointer-to-Int-node, we can just pass a raw machine integer. With a few passes of standard compilation technology (inlining +, etc) we wind up with the moral equivalent of while (n--) { i *= n; }.
Your ifac example makes perfect sense, thanks.
Stefan