"layered" language approach (was: Re: Constructors (was: RE: About KCP and automatic initialize))

Ian Piumarta ian.piumarta at inria.fr
Fri Sep 19 01:44:00 UTC 2003


Rice:
> Stef:
> > Yes the scheme macro for example. I would really like to know what is a
> > good macro system for a Smalltalk.
>
> For reference, Slate has been managing to simplify some language aspects
> such as is the case for its macro system:
> Where preceding a selector with ` will result in applying it to its'
> arguments parse objects

AFAIK, this "quasiquotation" was first *formalised* in Scheme (as part of
R4RS? around 1985 or so??).  That's one extreme.  At the other extreme it
has been applied to the C language in the form of "`C" ("tick C" --
curious how everyone chooses backquote ;) and its compiler "tcc", dating
from 1995 or so:

   http://www.pdos.lcs.mit.edu/tickc/

In "`C" a backquote preceding a statement or expression delays code
generation for that entire subtree in the AST until run time, and
offers selective, per-node replacement within it with values computed
at runtime.  Semantically, this is identical to quasiquotation
appearing within the body of a lambda expression in Lisp.

> (which may be evaluated early or template- transformed or
> directly-manipulated or left alone). Slate has other mechanisms as
> well for syntax extension, but they are all of this flavor: no
> special angle-bracket syntax for type declarations or primitive
> calls are needed.

This works well when the input language is capable of expressing the
result of all interesting syntactic transformations (by definition,
the input and output languages are the same).  More difficult (in the
absence of a real MOP, tightly integrated with a runtime compiler and
code generator) is extending the range of semantics available to the
program, and (more importantly) extending the range of pragmatic
influence the program can have directly on its external environment
(without having to go through some kind of FFI).

The usual incentive for "template transformation" type macros is that
they can be evaluated without having to compile and execute real code
within the compiler itself at compile time.  (They are friendlier
toward "static" compilers.)  Many Lisps (and Smalltalk) do not suffer
from this handicap at all.  Being able to apply quasiquotation within
a lambda expression (i.e., applying unconstrained rewrites over parts
of the live AST) to direct the final code generation with runtime
specialisations is a much simpler, far more general, and conceptually
transparent replacement for these more "formal", structure-transforming
macro "minilanguages".

> From water at tunes.org Thu Sep 18 19:13:40 2003
>
> In what follows, my answers are phrased in terms of the question which
> spoke about macros per se, but should be understood as applying to
> generative or meta-level programming, which has a more understandable
> relationship to Smalltalk than macros do per se. (For this reason, I found
> the specific questions to be inappropriate, but still worth answering.)

Macros per se (in any guise) *always* represent some form of
"generative programming".  They can be *implemented* either as
meta-level "semantic programs", or as simple rule-based "syntactic
transformations", or as trivial rule-based structureless "textual
substitutions".  (One can do very powerful generative programming
inside the C preprocessor -- yet cpp has no relationship whatsoever
with meta-level programming.)

So I think the questions were entirely appropriate.

(Don't forget that people not immersed daily in highly-generative
and/or meta level-revealing systems -- or who have used only systems
that deliberatly downplay the meta-level transformations effected by
user-level "macros" -- might (1) not entirely appreciate that what
their language calls "macros" are a form of limited exposure to real
semantic programming on "deep" structures within the implementation,
or might (2) really be talking about precisely that but lack practical
experience with a system that presents these features for what they
are, rather than just labeling them simply as "macros".)

> > (not the technical one
> > because we are good to access and solve it but the scalability and use
> > one).
>
> The way to deal with or comprehend macros is to grab the output or some
> intermediate form of it (using full or step-wise macro-expansion.. which
> is a standard lisp tool) and insert that output into your code. Of course,
> a good Lisp programmer never does this as such, preferring to write new
> macros for a refactoring and using the macro-expansions' comparison to
> debug/unit-test. Or, to write a new common macro that lets you express
> both the new and old pattern in it, and implement them both on top of
> that. It's just refactoring and XP applied at the meta-level.
>
> One reason that Lisp's tools aren't up to par with Smalltalk's (aside from
> the big economic collapse) is that the output code of macro-expansion is
> just a bunch of lists usually,

In Lisp, "just a bunch of lists" *is* an abstract syntax tree, formally
capable of expressing every language construct, and in which every source
expression is necessarily (and entirely) expressed.  Lists and atoms and
lists thereof not only necessary but also sufficient.  This is at the very
core of the theoretical foundations of Lisp.

   [John McCarthy, "Recursive functions of symbolic expressions and
   their computation by machine", CACM 3(4), April 1960.]

> so the important meta-information is not explicitly transmitted with
> it.

The choice of representation of any and all "meta information" rests
entirely with the language implementor.  If said person chooses to
represent it in some opaque fashion, unvailable in the meta-level
exposed to the programmer, then that's not a deficiency inherent in
"meta-programming" -- it's a deficiency inherent in the implementation
*design* of that particular language.  Had the designer chosen to make
the representation of "meta information" correspond either to
language's syntactic input structures, or to its user-domain data
structures (in Lisp these are the *same* thing), then there would not
be any reason whatsoever to lose "meta information".

   [Jim des Rivires and Brian Cantwell Smith, "The implementation
   of procedurally reflective languages", Proc. ACM Symposium on Lisp
   and Functional Programming, August 1984.]

> At some point, though, you are just talking about generative
> programming or meta-programming, and you just won't get a magical
> ability to translate between different custom extensions

Meta-programming is *precisely* all about getting out of the
user-level box and into the implementation-level box where all the
interesting customisations are done.  The *extreme* case would be
this: if the meta-level lets you generate any machine code
instructions you like in response to seeing some particular syntactic
input structure, there is no limit on the range of the resulting
semantics (including infinitely many illegal ones).  Moreover, if the
correspondance between input structures and generated code is driven by
rules expressed as "meta-level" structures (defined by those "custom
extensions") then translating between them is *entirely* possible.

To any sufficiently primitive language design I suppose this *might*
look like MAGIC.

> can know about these effects and handle them well. Dylan's Functional
> Developer seems to have done the best in the tools area.

Dylan has to have good tools (if only to cope with the heaps of
irrelevant syntax they're wallowing in ;).

> From daniel.a.joyce at worldnet.att.net Thu Sep 18 19:13:54 2003
>
> Sounds like Clean/Haskell, in which nearly all features of the language are
> implemented in the prologue as rewrite rules. At the lowest levels, there are
> only a few simple operators. Every other language feature is the result of
> rewrite rules being applied to functions....

Yes!  This is How It Was Meant To Be.  You can probably trace this back
even further, but the first similar approach I know of was in a Scheme
system called T (or was it Tea?) and its compiler called Orbit (sometime
around the mid-80s?).  The language had almost no special forms.  The
principle was simplicty incarnate: reduce absolutely *everything* to
lambda expressions, then dedicate 98% of your compiler to optimising the
hell out of lambdas.

Ian



More information about the Squeak-dev mailing list