[Sorry for late response...]
Pascal Costanza <firstname.lastname@example.org> wrote:
| a) We don't need macros. We can program everything in assembler.
Please don't assume this is an either/or choice! There *are* (or at
least, historically, have been) assemblers which had very close to
DEFMACRO-like capabilities -- MACRO-10 (the assembler for the PDP-10)
is a sterling example. Macros in MACRO-10 could do compile-time looping
and branching, could tear apart lists of arguments and individual
arguments (considered as lists of characters), could generate new
symbols based on arguments provided, could define/set/modify compile-time
variables (including those whose names were generated by macros),
and since MACRO-10 was a two-pass assembler that provided the ".if1"
and ".if2" tests, macros could gather information during pass one
(storing it in compile-time variables/symbols) and during pass two
emit local code chosen based on the *global* contents of the program!
I have written elsewhere [Google: FOCAL MACRO-10 rpw3] how useful
this is for writing lexical and syntax analysers, but it's also
useful for building complex graphs of objects at compile time,
planting all the needed links (pointers) between them with no
need for run-time initialization of the data structures.
| It's important to keep in mind that all programming languages only
| and purely make things convenient, not possible. It's already possible
| to program anything in any language, so there's nothing to gain here.
Don't discount "convenience", which has a very real value in
accelerating development and simplifying the code and thus
lowering both the initial and the ongoing maintenance costs!
| The important gain of macros is that they allow you to remove
| boilerplate code. There are other means to remove boilerplate code,
| but few are as effective and convenient as (structural) macros.
While macros for boilerplate code are certainly part of it, to me
an equally-important part is allowing the programmer to decide --
right in their source code -- which parts of the computation are
to be done at compile time (well, macroexpansion time) and which
are deferred to runtime.
These two aspects work together, as for things mentioned above.
E.g., a DEFOPERATOR macro can provide convenient boilerplate for
defining some operator and related attributes, but at the *same*
time it can insert information about the operator into data structures
that are later available to the program, e.g., a symbol table, an
operator-precedence table, a code-to-emit table, etc.
| b) It's hard to illustrate features of a language that tend to show
| their strength only in large programs.
| Of course, this is bad news for Lisp from a publicity point of view.
Rob Warnock <email@example.com>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607