Strictness annotations are not annotations, since they change the
meaning of a program. Let's use the term strictness indicators.
As I mentioned in an earlier message to this mail group, with
> f (Pair a b) = b
the value of (f (Pair x 5)) may not be 5, when Pair involves strictness
indicators. I think this is very similar to the problems that lead to
the removal of views from Haskell and laws from Miranda. These problems
are discussed more in [1].
One solution is as follows. When strictness indicators are used in a
data declaration, then corresponding indicators must be used in
expressions, but not patterns. For example, if we declare
> data PairType a b = Pair !a b
then the above function definition remains valid, but (f (Pair x 5))
is no longer a valid expression. Instead, (f (Pair !x 5)) is required.
Now the expression does not exactly match the left side of the equation,
so its value is not "obviously" 5.
This need not be as awkward as it first appears, since a programmer can
define a function
> pair a b = Pair !a b
that encapsulates the strictness indicator. However, there is one
remaining serious problem with this approach. Constructors can no
longer be partially applied or treated as ordinary functions, since we
have an obligation to attach a strictness indicator to certain
arguments.
The solution is to attach the strictness indicator to the constructor
itself, and require that if a constructor is strict, then it is strict
in all arguments. This is commonly the case (e.g. with abstract types
where a constructor has only one argument, or with complex numbers).
This results in the following:
> f (Pair a b) = b
> data PairType a b = !Pair a b
> x = ...
> y = f (!Pair x 5)
where the ! in the definition of y clues us to the added strictness.
Now !Pair is a function that can be used to construct values, but not a
constructor that can be used in patterns. On the other hand, Pair is a
constructor that can be used only in patterns.
If we really need a constructor that is strict in some arguments but not
other, this is still possible indirectly. For example, with
> data Lift a = Lift a deriving (Data)
> data (Data a) => PairType a b = !Pair a (Lift b)
> y = !Pair (f x) (Lift f)
the evaluation of y causes the evaluation of (f x), but not f. Data is
the class, proposed by Simon, of types that can be the targets of
strictness indicators.
I don't like the added complexity if having to use the ! in expressions,
but see no alternative, including the status quo, that isn't worse.
1. Burton, F. W. and Cameron, R. D. Pattern matching with abstract
data types. Journal of Functional Programming 3, 2 (April 1993),
171-190.
Warren Burton
Simon Fraser University