[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: macros vs. blocks



Michael Sperber wrote:
> While everything else you say is true, SSAX:make-parser is a poor
> example for an intrinsic use of macros: it's a macro purely for
> efficiency reasons (at least it was the last time I looked at it) and
> has a trivial counterpart in procedural form.

You could have put it a bit more gently!  Whose side are you on?  ;)

Performance is one of the purposes of the macro in this case, but it's not
the only one.  The SSAX:make-parser macro takes up to seven named callbacks
as arguments.  Not all of the callbacks have to be supplied; defaults will
be used for those that are omitted.  The call looks something like:

(SSAX:make-parser
  NEW-LEVEL-SEED (lambda (...) ...)
  UNDECL-ROOT (lambda (...) ...))

...where the capitalized names name the subsequent callback procedure.

This design serves at least two purposes, which I'll describe before
comparing to the alternatives:

First, it provides a convenient interface for constructing parsers.  The
host language here, Scheme, doesn't intrinsically have named arguments to
procedures, but that feature has been created for this macro, and used to
provide three of the usual benefits of named arguments: the ability to omit
arguments, to list them in arbitrary order, and the improved readability
that can come from explicitly naming them.  Together, this allows a complex
parser to be constructed quite flexibly, with a single expression, while
retaining comprehensibility.

Second, as noted, the macro has a performance purpose.  If the supplied
callbacks are defined inline, then they will be expanded inline within the
macro, which allows optimizations that would not be possible otherwise.  The
design of the macro is such that the callbacks are only used in one place,
where arguments are provided to them.  This means that the effect of
inlining them is that the arguments are bound to the callback's parameters,
resulting in the callback procedure disappearing during compilation, to be
replaced by an inline block with local variables, which can be quite a bit
more efficient (depending on the language implementation).  Callbacks with
no arguments would be completely inlined, eliminating the closure.

The first purpose, using a macro to create a convenient interface, can be
achieved some other way - although in a language without optional named
arguments (or an equivalent feature, e.g. the Smalltalk chained message
approach), the caller would need a bit of machinery to set this up.  In an
OO language, you might inherit from a parser class and override some of the
methods.

In this case, the macro allows SSAX:make-parser to achieve an elegant and
concise end result.  The invoking program doesn't have to introduce and use
a lot of machinery in order to generate a custom parser - it just needs a
single expression, it doesn't have to define a class, or a bunch of
separately defined callback procedures, etc.  Making life simple for the
caller is one of the points of macros, and this has been achieved here.

Now, on to performance: some people have suggested that this is unimportant.
That may be true in the typical applications for which most of the
lightweight/scripting languages are used.  This ignores many real-world
cases, though.  Over the long term, perhaps we'll just be able to completely
forget about performance; in my experience, that hasn't happened yet, in
many application domains.  In some, it won't ever happen, short of quantum
computing: processing the results from particle accelerators, for example.
Often, when a language proves too slow for a given task, the solution is to
convert the code into a faster, but usually lower-level language, such as C
or C++.  But surely it would be preferable, if possible, to continue to use
higher-level, safer languages, even when maximum performance is required?

Well, it is possible.  Languages like the ML family, Lisp, and Scheme are
all capable of performance very close to that of C (as evidence, I'll offer
http://www.bagley.org/~doug/shootout/craps.shtml).  I'm not pointing this
out to evangelize particular languages: the more fundamental point is that
high-level capabilities can be had with minimal loss of performance, and
that this can be a benefit in the real world.

Perhaps many people on this list don't often run into applications where
performance is an issue, but they're not all that uncommon.  I don't think
"improved performance" is a feature that can be automatically dismissed out
of hand.  A particular language might choose not to pursue performance as a
goal, although from what I've seen, even in the implementations of Perl &
Python etc., plenty of attention has been paid to performance.

Macros can help with performance, and it's up to the user of the language to
choose when it makes sense to use them for that purpose.  In the SSAX
parser, their use for this purpose is sensible - parsing very large XML
files is one of the less esoteric areas in which performance can be an
issue.

Anton