On Mon, Jan 30, 2012 at 6:24 AM, Malcolm Wallace
<malcolm.wallace@me.com> wrote:
On 29 Jan 2012, at 22:25, Ertugrul Söylemez wrote:
> A strict-by-default Haskell comes with the
> implication that you can throw away most of the libraries, including the
> base library. So yes, a strict-by-default Haskell is very well
> possible, but the question is whether you actually want that. I
> wouldn't, because a lot of my code relies on the standard semantics.
At work, we have a strict version of Haskell, and indeed we do not use the standard libraries, but have built up our own versions of the ones we use. However, our compiler is smart enough to transform and optimise the source code *as if* it were non-strict: it is only at runtime that things are evaluated strictly. This means that, in general, you can rely on the standard semantics to a surprisingly large extent.
I wanted to emphasize Malcolm's point here. Optimizing using the original Haskell semantics turned out to be pretty important back when I was working on Eager Haskell. For example, a lot of Haskell functions are written in the following style:
f a b c
| guard1 d = g h i
| guard2 e = h
| guard3 f = i
| otherwise = j
where d = ...expensive...
e = ...expensive...
f = ...expensive...
g = ...expensive...
h = ...expensive...
i = ...expensive...
j = ... expensive...
An an ordinary procedural language, where function calls in g, h, i, and j might have side effects, we can't sink bindings down to the point of use. Even in the absence of side effects, we have to account for the fact that some of these computations are used in some -- but not all -- right-hand sides, and that often we need to do some inlining to discover that a value isn't going to be used. It turns out Haskell code relies on this sort of thing all over the place, and simply coding around it leads to deeply-nested let bindings that walk off the right-hand edge of the screen.
It's not difficult to rewrite most of the prelude functions in this style, but it's no longer pretty, and it's recognizably not idiomatic Haskell.
However, bulk operations do transform the entire data structure, not merely the fragments that are needed for the onward computation, so it can often be a net performance loss. The standard lazy computational paradigm of generate-and-test is therefore hideously expensive, on occasion.
This was a huge issue in Eager Haskell. By far our worst performance was on stream-like programs that generated infinite lists of results, and then sliced away the useless tail. With completely strict evaluation, this of course doesn't work at all, but it can be tricky to bound the required sizes of inputs even if you know how much of the output you want (imagine a call to takeWhile or filter on an infinite list).
-Jan-Willem Maessen
Regards,
Malcolm