Language extension proposal
Andrew J Bromage
ajb@spamcop.net
Wed, 25 Jun 2003 14:17:51 +1000
G'day all.
I've been toying with an idea for a small language extension, and I
was curious what you all think of this.
As a motivating example, consider the following standard typeclass:
class Bounded a where
min :: a
max :: a
This is an example of what I refer to as a "traits typeclass" (by
analogy to traits classes, well-known to C++ programmers). The idea
is that you want to specify certain properties/operations which depend
on a _type_, rather than any particular value of that type.
Another example is floating point format information, like the
information in C's <float.h>. One might implement this as:
class (Bounded a) => FloatTraits a where
epsilon :: a -- OK
mantissaDigits :: Int -- Not OK!
For obvious reasons, the declaration of mantissaDigits is not valid
Haskell. Instead, you might do something like this:
class FloatTraits a where
mantissaDigits :: a -> Int
instance FloatTraits Float where
mantissaDigits _ = 24
instance FloatTraits Double where
mantissaDigits _ = 53
and invoke it as, say:
mantissaDigits (undefined :: Float)
A decent strictness analyser can tell if an argument to some function
is always unused (in GHC core terminology, this means its demand is
"absent") and thus avoid passing the argument at all. However, it is
currently impossible to tell if this is true all instances of a
typeclass. What I would like is some annotation that I can use to tell
my compiler that this argument of this method is always unused, no
matter what instance it resolves to.
My proposal is to grab a new punctuation symbol (I will use '~' as
an example) to sit alongside the GHC extension use of '!'. Where '!'
means that an argument may be evaluated before calling a function, '~'
means than an argument may be ignored by the callee. So in the above
example, I can write:
class FloatTraits a where
mantissaDigits :: ~a -> Int
Now this marker is not as simple to implement as '!'. The strict
marker is advisory; if you stripped all '!'s from your program, it
would still have the same static semantics. Not so with '~', which
must be statically checked.
However, the static analysis is pretty trivial, except for the
problem of inference. Basically, if a variable has absent demand,
it a) can't be pattern matched on (unless the pattern is a variable,
of course), and b) can't be passed as an argument to a function
(including a data constructor) which doesn't also treat that argument
as having absent demand.
I therefore propose that the marker not be inferred for top-level
definitions, or at least for exported definitions. If you declare it,
it's checked, but otherwise it's not. In addition, any calls that you
pass a variable with absent demand to must themselves have the marker
declared explicitly in their type signatures.
There may also be an argument for extending '~' to data constructors,
in the same way that H98 uses '!'. This would allow parameters with
absent demand to be stored in data structures. I can't think of a
use for this right now, but it shouldn't be too much harder to add.
What do you all think? Useful? Not worth the effort?
Cheers,
Andrew Bromage