#ifdef considered harmful
alastair at reid-consulting-uk.ltd.uk
Wed Apr 7 02:09:45 EDT 2004
> Will such a preprocessor work on source code like cpp does or on a syntax
> tree? The latter would be better, IMO; the former would probably have
> little or no advantage over cpp.
A bunch of people have answered this already but your comment reminded me of
one of the cpp problems we might want to avoid (by omitting a large chunk of
cpp functionality). One of cpp's flaws is that it has absolutely no notion
of scope so if you define a macro 'bar' then code like this can do surprising
foo bar = baz
What do people use macros for?
1) Aliases and abstraction:
Macros are useful in C because C is quite poor at defining aliases for
existing things. (e.g., typedefs can't be parameterized, you can't define an
alias for a variable, creating an alias for a function is expensive unless
you (can) mark it inline).
Haskell has great ways of creating aliases for things: type synonyms and
function definitions. There's no strong need for more. (A few exceptions
listed at the end of this mail.)
2) Defining symbols to be used in #ifs
This is useful for conditional compilation in both C and Haskell.
So, I'd like to float the idea that the Haskell preprocessor should _only_
support conditional compilation, that macro expansion should only occur in
lines that begin with '#' not in "normal" lines and that macros should always
evaluate to expressions with simple scalar types (ints, strings, etc.)
Similarly, I'd like to drop token splicing (e.g., foo##bar in ANSI C or
foo/**/bar in K&R C). It's mostly useful for overcoming limitations in C and
isn't much used to help you write #ifs.
These two changes make it possible to replace the usual horrible mix
of macro expansion, parsing and evaluation with a conventional interpreter.
#defines assign values to variables and
#if evaluates expressions involving those 'variables'.
I'm not sure if it would still be useful to be able to define parameterized
#define max(x,y) ...
but I doubt it would cost much to allow it.
1) hspp implementation is a lot simpler.
[It's probably simple enough that the preprocessor could
easily be built into Hugs if we wanted. This isn't the primary
motivation for this suggestion but it would be nice.]
2) hspp semantics easier to understand so programmers not caught
out by name capture and all the other problems that cpp can
3) hspp only affects lines that start with # in the first column.
This further lessens chance of programmer being caught out.
4) No need to add extra parentheses round every use of a macro argument
and round macro bodies as in:
#define max(x,y) ((x)>(y)?(x):(y))
There's only one place I know of where this restriction would have any
significant effect. The FFI libraries have a lot of very similar definitions
for all the new FFI types to define the Storable instances. IIRC, a cpp
macro is used to significantly reduce the amount of code that has to be
written and to make it easier to read/maintain. It would be a shame to lose
this use of cpp but, if this is the only loss, I think it would be worth it.
Alastair Reid www.reid-consulting-uk.ltd.uk
ps Inessential syntax tweak: If we are willing to change cpp syntax instead of
just restricting it, we might require "=" between the lhs and rhs of a macro
definition. This makes parsing of macros a little easier. cpp macros are a
bit funny in that you get very different results if you write a space between
the macro name and the arguments. For example:
#define max(x,y) x>y?x:y
#define max (x,y) x>y?x:y
The first is a parameterized macro, the other isn't. With this change, the
following would be equivalent.
#define max(x,y) = x>y?x:y
#define max (x,y) = x>y?x:y
More information about the Libraries