Contents

Top


More Haskell Notes and Examples

Strictness Flags in Data Declarations

Albert Y. C. Lai, trebla [at] vex [dot] net

In a data declaration, you can flag some fields with ! as “strict”. For example, here is a data type with two Int fields, where the second one is flagged as strict:

data J = Ctor Int !Int

Operational Story

“Operational” means computational steps. Most Haskell compilers use lazy evaluation for many (but not all) computational steps. Beware that the Haskell standard does not require lazy evaluation, in fact it mostly speaks denotationally. (See the next section for the denotational story.) So the operational story is only true for now, not future-proof. But where this story is true, the strictness flag requests less laziness.

The first field of Ctor does not have a strictness flag, so it is evaluated lazily as usual. If I put a problematic expression there, then go on to avoid it, no problem happens.

case Ctor (div 1 0) 5 of
     Ctor _ n -> n

It evaluates to 5 successfully.

The second field of Ctor has a strictness flag. It is evaluated more eagerly. If I put a problematic expression there, it is more likely to cause problems, and earlier.

case Ctor 5 (div 1 0) of
     Ctor m _ -> m

I already try to avoid the problematic expression, but it will still cause failure.

The precise criterion: The strict field is evaluated when the parent is evaluated (for example I used pattern matching to evaluate the parent).

To show the flip side of the criterion, here is an example of bearing the problematic expression but still causing no problem, since I avoid evaluating the parent, I only build it, give it a name, then give it more names.

let x = Ctor 5 (div 1 0)
in
case x of
     v -> ()

Denotational Story

“Denotational” means you don't describe computational steps (the “how”), only whether you will get an answer, and if so, what answer (the “what”). (Also, it means defining answers by structural induction on expression trees.) The Haskell standard is mostly denotational, and “strict” is a denotational notion, not an operational notation.

This is bound to be lost on most people, since they cannot imagine that there could be any story to tell other than computational steps, or any story at a higher level. Therefore I began with the operational story first, even though it is neither universal nor future-proof.

The symbol ⊥ stands for “no answer”. Where there is no answer, we still like to ironically say “the answer is ⊥”, for covenience and uniformity. It is why we make a symbol ⊥ for this.

A strict field means that we want this: If the field has no answer, then this causes the parent to have no answer either. Example: div 1 0 has no answer, so if we put it in a strict field, the parent Ctor 5 (div 1 0) has no answer either. Again, we also say: the answer of div 1 0 is ⊥, so the answer of the whole Ctor 5 (div 1 0) is also ⊥.

The negation of “strict” is called “non-strict”. A field without a ! flag is a non-strict field, which means that even when the field has no answer, we still consider the parent to have an answer other than ⊥. Example: The answer of Ctor (div 1 0) 5 is Ctor ⊥ 5, and this does not equal ⊥. (This looks boringly tautological, but this is only because we are talking about data, there is nothing to simplify further. Strict functions and non-strict functions will be more interesting.)

To better observe their difference, it is useful to know the denotational story of pattern matching against a data constructor. The answer of this pattern-matching expression

case expr of Ctor m n -> body

depends on the the answer of expr:


I have more Haskell Notes and Examples