# Metaprogramming & language design [Was Re: [stack] Digest Number 28]

## Mark van Gulik — 2000-06-07 04:55:47

    From: Massimo Dentico <m.dentico@...>

     > Sorry for the delay Mark, but I'm busy in these days and I need much time

    > to write in English. Some subjects are already addressed by Bill, I agree

    > with him thus I do not intend to be redundant.

    >

    > Mark van Gulik wrote:

    >>

    >> Massimo Dentico <m.dentico@...> wrote:

    >> > Mark van Gulik wrote:

    >> >> I'm positive that Forth is applicable over an enormous range of domains,

    >> >> mostly due to its core simplicity and extreme extensibility.  I'm mostly

    >> >> concerned, however, with the ability to build *restrictions* in the

    >> >> language.

    >> >

    >> > I completely agree with this. In particular, the key concept is

    >> > ".. the ability to build *restrictions* in the language". This means

    >> > that *you*, the programmer, build the restrictions when you really

    >> > need it and it's not the language designer that in arbitrary way

    >> > restricts you.

    >>

    >> I almost agree.  The language designer also has the responsibility of

    >> building the right restrictions *into* the language, such that it will have

    >> clean semantics and high reliability (the two tend to go together).  For

    >> example, in another thread I argued that conservative garbage collection

    >> (CGC) in Forth is really CGC in some proper subset of Forth.  That's because

    >> Forth's semantics are too strongly defined in terms of machine

    >> representation to allow CGC (in the full language).  A redesign of Forth is

    >> possible to accomodate CGC for the full language, but it wouldn't look much

    >> like Forth afterwards.  Basically issues like GC, type systems,

    >> optimization, and design-by-contract must be *designed into* a language, not

    >> added later (typically by subtractive synthesis as with CGC and retrofitted

    >> type systems).

    >

    > I disagree here. The advantage of a meta-programming (constructing

    > programs that inspects/manipulates other programs) tool with reflective

    > capabilities (ability to inspect/modify itself even if, in Forth, is fairly

    > ad-hoc) like Forth is exactly this: the tool have the ability to absorb and

    > arrange disparate syntaxes/semantics.

     Suppose you want to find all calls to some routine.  An integrated

    development environment like Smalltalk allows you to do this search via

    metaprogramming.  I agree that this is a good thing.  On the other hand, the

    compiler in Smalltalk can be modified (each class can use a different

    Compiler class to compile the class's methods).  That is probably a bad

    thing, because it means the code that searches through the library for all

    calls to some routine might not work any more.  Or maybe the browsers stop

    working, or exception handling breaks because of assumptions about the

    compiler, etc.  If you define a clean, generic protocol for the Compiler

    class you can avoid some of these problems, but this is just language

    design!

    I'm in favor of lots and lots of metaprogramming.  I'm *not* in favor of

    leaving the system so open to fundamental changes that you can no longer

    trust a single line of code to do what you expect it to.  Some kinds of

    metaprogramming are this severe.

    Consider optimization.  If a wide-open metaprogramming system allows the

    compiler to be augmented with new code to perform optimizations, this is

    probably a Bad Thing.  Without strict rules or strongly-worded guidelines

    about what invariants an optimization must maintain, someone might add an

    optimization that breaks the ability to do symbolic debugging, or breaks the

    ability to search for all calls to a routine.  It would be much better if

    the rules were made very explicit.  In that sense, the optimization

    framework would deal with the metaprogramming stuff, and present a safe,

    simplified interface for user-supplied optimization rules to use.

    Treating most of the metaprogramming facilities as part of a language rather

    than merely a library, I believe the language designer must be *very*

    careful about what's available in the metaprogramming interface.  Water is

    very, very flexible, but it's only when you remove most of this flexibility

    by freezing it that you can build useful things from it.

     > For example, redefining the semantics of some words (creating new contexts

    > for old programs) probably it's possible to accommodate GC better than in

    > traditional languages (without GC); it's even possible to imagine a

     metaprogram

     > that takes as input a Forth program with direct memory management and gives as

    > output a Forth program integrated with GC. Some research on static (compile-

    > time) automatic memory management exists also.

     The safest way to support GC (by far) is to *design* it into the language at

    the beginning.  It's almost impossible to add it safely to a language, but

    it's almost trivial to include it in a new language.

     > However, I consider Forth more a conceptual model than a language in itself:

    > it's defined more by "concepts" like words, staks, dictionaries, text input

    > buffer than by a particular set of primitives.

     Ok, but then I'll just change what I say should have metaprogramming

    restrictions:  "Forth + its primitive words would benefit from having

    restrictions of the form...".

## Massimo Dentico — 2000-06-08 04:13:39

    Mark van Gulik on Concatenative mailing list wrote:

     >

    > From: Massimo Dentico <m.dentico@...>

    > > Sorry for the delay Mark, but I'm busy in these days and I need much time

    > > to write in English. Some subjects are already addressed by Bill, I agree

    > > with him thus I do not intend to be redundant.

    > >

    > > Mark van Gulik wrote:

    > > >

    > > > Massimo Dentico <m.dentico@...> wrote:

    > > > > Mark van Gulik wrote:

    > > > > > I'm positive that Forth is applicable over an enormous range of domains,

    > > > > > mostly due to its core simplicity and extreme extensibility.  I'm mostly

    > > > > > concerned, however, with the ability to build *restrictions* in the

    > > > > > language.

    > > > >

    > > > > I completely agree with this. In particular, the key concept is

    > > > > ".. the ability to build *restrictions* in the language". This means

    > > > > that *you*, the programmer, build the restrictions when you really

    > > > > need it and it's not the language designer that in arbitrary way

    > > > > restricts you.

    > > >

    > > > I almost agree.  The language designer also has the responsibility of

    > > > building the right restrictions *into* the language, such that it will have

    > > > clean semantics and high reliability (the two tend to go together).  For

    > > > example, in another thread I argued that conservative garbage collection

    > > > (CGC) in Forth is really CGC in some proper subset of Forth.  That's because

    > > > Forth's semantics are too strongly defined in terms of machine

    > > > representation to allow CGC (in the full language).  A redesign of Forth is

    > > > possible to accomodate CGC for the full language, but it wouldn't look much

    > > > like Forth afterwards.  Basically issues like GC, type systems,

    > > > optimization, and design-by-contract must be *designed into* a language, not

    > > > added later (typically by subtractive synthesis as with CGC and retrofitted

    > > > type systems).

    > >

    > > I disagree here. The advantage of a meta-programming (constructing

    > > programs that inspects/manipulates other programs) tool with reflective

    > > capabilities (ability to inspect/modify itself even if, in Forth, is fairly

    > > ad-hoc) like Forth is exactly this: the tool have the ability to absorb and

    > > arrange disparate syntaxes/semantics.

    >

    > Suppose you want to find all calls to some routine.  An integrated

    > development environment like Smalltalk allows you to do this search via

    > metaprogramming.  I agree that this is a good thing.  On the other hand, the

    > compiler in Smalltalk can be modified (each class can use a different

    > Compiler class to compile the class's methods).  That is probably a bad

    > thing, because it means the code that searches through the library for all

    > calls to some routine might not work any more.  Or maybe the browsers stop

    > working, or exception handling breaks because of assumptions about the

    > compiler, etc.  If you define a clean, generic protocol for the Compiler

    > class you can avoid some of these problems, but this is just language

    > design!

    >

    > I'm in favor of lots and lots of metaprogramming.  I'm *not* in favor of

    > leaving the system so open to fundamental changes that you can no longer

    > trust a single line of code to do what you expect it to.  Some kinds of

    > metaprogramming are this severe.

     Recapitulation: we agree about metaprogramming as a good thing;

    we agree about the necessity to express restrictions in metaprograms,

    better if into the language itself; we disagree about built-in

    restrictions.

     > Consider optimization.  If a wide-open metaprogramming system allows the

    > compiler to be augmented with new code to perform optimizations, this is

    > probably a Bad Thing.

     There is a system that do this, Pliant. The author differentiates

    optimizations done only at the intermediate level (machine indipendent)

    and machine specific optimization:

    Pliant - release XX (current is 39)

    - http://pliant.cx/

    in particular "The Pliant language specifications"

    [..] "Meta programming: the great step":

    - http://pliant.cx/pliant/language/

     > Without strict rules or strongly-worded guidelines about what invariants

    > an optimization must maintain, someone might add an optimization that

    > breaks the ability to do symbolic debugging, or breaks the ability

    > to search for all calls to a routine. It would be much better if the

    > rules were made very explicit.

     Exactly, *very explicit*, not implicit in the language.

     > In that sense, the optimization framework would deal with the metaprogramming

    > stuff, and present a safe, simplified interface for user-supplied optimization

    > rules to use.

    >

    > Treating most of the metaprogramming facilities as part of a language rather

    > than merely a library, I believe the language designer must be *very*

    > careful about what's available in the metaprogramming interface.

     What I propose is to put metaprogramming facilities *and* restrictions

    into libraries, reconciling flexibility, extensibility with safety.

    This is not a problem if you have the ability to build restrictions in

    the language itself, you don't need another notation.

    What about if you build into the language some restrictions that you

    consider absolute necessary to ensure a certain safety level and in the

    future someone discovers a better way to ensure the same safety level but

    with less restriction to expressiveness? (This could be related to the

    Turing-equivalence of such powerful type system, thus undecidability

    creeps in ... but I'm not qualified to explore this subject: volunteers

    are more than welcomes).

     > Water is very, very flexible, but it's only when you remove most of this

    > flexibility by freezing it that you can build useful things from it.

     In your metaphor: no, it's sufficient (and often more useful) to build

    conduits (restrictions). You can even transform water in vapor, removing

    some restrictions and enforcing other restrictions.

     > > For example, redefining the semantics of some words (creating new contexts

    > > for old programs) probably it's possible to accommodate GC better than in

    > > traditional languages (without GC); it's even possible to imagine a metaprogram

    > > that takes as input a Forth program with direct memory management and gives as

    > > output a Forth program integrated with GC. Some research on static (compile-

    > > time) automatic memory management exists also.

    >

    > The safest way to support GC (by far) is to *design* it into the language at

    > the beginning.  It's almost impossible to add it safely to a language, but

    > it's almost trivial to include it in a new language.

     The safest way to support GC, in my opinion, is to *demonstrate* that

    the algorithm is safe. With metaprogramming you could combine a specific

    program with a specific GC algorithm (better for this program, perhaps

    obtained from a more general GC via some specialization technique).

    Given the context (the program), it could be more simple to demonstrate

    GC algorithm safety against this program rather than an unknown context

    (every possible programs). The idea of using different GC algorithms at

    the same time for different program is certainly not new.

    However, I'm not an expert about GC and formal treatment of programs,

    thus I'm unable to establish the possible difficulties in this procedure.

    I admit that, with your strategy, metaprogramming is more approachable by

    the "average" programmer, but this doesn't mean that the situation will

    never change (after all, freedom implies responsibility).

     > > However, I consider Forth more a conceptual model than a language in itself:

    > > it's defined more by "concepts" like words, staks, dictionaries, text input

    > > buffer than by a particular set of primitives.

    >

    > Ok, but then I'll just change what I say should have metaprogramming

    > restrictions:  "Forth + its primitive words would benefit from having

    > restrictions of the form...".

     I could speak about the CMoF (Conteptual Model of Forth) .... :)

    About Forth primitives, they are not necessary if you give a meta-circular

    definition of the language, choose carefully a subset of words necessary

    to define it, the language kernel (usually those words with a mapping 1:1,

    or near, to machine instructions) and then define the rest of the language

    in term of this subset (it's not so difficult if you write, as usual in

    Forth, mostly functional words, without side effects). In this way the

    kernel can varies and you can adapt it to different hardware (not a fix set

    of primitives) but the language (or the system) its always the same.

    Probably you know all that: if I remember well, this is common practice

    in the Forth community.

    This "equational" treatment is even more evident in Joy, where side

    effects are absent and definitions uses the symbol "==".

    --

    Massimo Dentico