It's a strictness declaration. Basically, it means that it must be evaluated to what's called "weak normal head form" when the data structure value is created. Let's look at an example, so that we can see just what this means:
data Foo = Foo Int Int !Int !(Maybe Int)
f = Foo (2+2) (3+3) (4+4) (Just (5+5))
The function f
above, when evaluated, will return a "thunk": that is, the code to execute to figure out its value. At that point, a Foo doesn't even exist yet, just the code.
But at some point someone may try to look inside it, probably through a pattern match:
case f of
Foo 0 _ _ _ -> "first arg is zero"
_ -> "first arge is something else"
This is going to execute enough code to do what it needs, and no more. So it will create a Foo with four parameters (because you can't look inside it without it existing). The first, since we're testing it, we need to evaluate all the way to 4
, where we realize it doesn't match.
The second doesn't need to be evaluated, because we're not testing it. Thus, rather than 6
being stored in that memory location, we'll just store the code for possible later evaluation, (3+3)
. That will turn into a 6 only if someone looks at it.
The third parameter, however, has a !
in front of it, so is strictly evaluated: (4+4)
is executed, and 8
is stored in that memory location.
The fourth parameter is also strictly evaluated. But here's where it gets a bit tricky: we're evaluating not fully, but only to weak normal head form. This means that we figure out whether it's Nothing
or Just
something, and store that, but we go no further. That means that we store not Just 10
but actually Just (5+5)
, leaving the thunk inside unevaluated. This is important to know, though I think that all the implications of this go rather beyond the scope of this question.
You can annotate function arguments in the same way, if you enable the BangPatterns
language extension:
f x !y = x*y
f (1+1) (2+2)
will return the thunk (1+1)*4
.