Re: [stack] On comments 1..6

Louis Madon — 2000-09-05 07:41:50

phimvt@... wrote:

> Comments - 6
> I had considered a certain extension of Joy, something like
> a macro preprocessor. Louis said it would not be necessary because
> "Joy is its own macro language". While it is true that Joy can
> manipulate its own programs, that isn't quite the same as being
> its own macro language. In a later message I suggested looking at
> M4 as a suitable testbed. (Whether M4 is the best is debateable,
> but if it was good enough for Ratfor, then it is good enough for
> some experiments here).
> For ease of reading, I'll define macro names with a capital
> letter. Then the joy definition of square and the M4 definition
> of Square look like this:
> square == dup *
> define(Square, dup *)
> Both can be used in the same way:
> 3 square == 9
> 3 Square == 9
> But Square is an in-line version of square. For example
> [square] size == 1
> [Square] size == [dup *] size == 2
> Some people want inline definitions for efficiency, since it saves
> a call. I am not all that keen on it.
> But there may be other uses of M4 style macros, especially
> for definitions. Consider
> n1 == ...x1...
> n2 == ...x2...
> where the dots represent some fairly complicated stuff. With M4
> definitions that use parameters ($1, $2 and so on), such definitions
> look like this
> define(N, ...$1...)
> n1 == N(x1)
> n2 == N(x2)
> The alternative in "pure" Joy is to have
> n == shuffle x into the right position inside ...x...
> n1 == x1 n
> n2 == x2 n
> All the shuffling has to take place at run time, for every call.
> (Some of my own programs do this - e.g. mk_qsort in STDLIB.JOY.)
> It may be possible to enter things into the symbol table after
> some preliminary (="compile time") processing, but I do not yet
> see through all this too clearly. Maybe somebody will want to
> have a look at using M4 in detail. _If_ it turns out useful,
> one should investigate a taylor-made preprocessor for Joy.

So if I understand, your key point is that macros are needed for
efficiency reasons (you do not seem to be claiming that macros increase
expressiveness for example). Specifically, macros would be useful in
non-optimising compilers as a way of minimising runtime overhead.
Conversely, in an optimising Joy compiler that finds and evaluates
invariants at compile time they would serve no useful purpose. Does this
seem right, or did I miss something?


--
Louis.

phimvt@lurac.latrobe.edu.au — 2000-09-07 03:02:48

On Date: Tue, 05 Sep 2000 17:41:50 +1000
Louis Madon <madonl@...> wrote

>> It may be possible to enter things into the symbol table after
>> some preliminary (="compile time") processing, but I do not yet
>> see through all this too clearly. Maybe somebody will want to
>> have a look at using M4 in detail. _If_ it turns out useful,
>> one should investigate a taylor-made preprocessor for Joy.
>
>So if I understand, your key point is that macros are needed for
>efficiency reasons (you do not seem to be claiming that macros increase
>expressiveness for example). Specifically, macros would be useful in
>non-optimising compilers as a way of minimising runtime overhead.
>Conversely, in an optimising Joy compiler that finds and evaluates
>invariants at compile time they would serve no useful purpose. Does this
>seem right, or did I miss something?
>
Generally my concern is not all that much about machine efficiency,
but of course I grant that some people emphasise it for good reasons
in their applications. Inlining is just one instance, and a preprocessor
would save some run time. In my prototype the Joy version square would
need 3 calls (square, dup, *), and the M4 version Square would need
2 calls (dup *). That may be a worthwhile saving.

But macros can be used for many other things. I see Joy syntax as
an essential part of the language (more than in most other languages),
so I do not want to use M4 macros to change the syntax. But one
could use it for many other purposes which deserve investigation.
One of them is for packages. Suppose you need almost identical
sets of definitions for handling strings and list (which collectively
I call sequences. But for strings and lists you would need to use
the empty string and the empty list in the two sets of definitions.
Most other things are the same because things like cons, first,
rest, concat .. are sufficiently polymorphic. Consider the M4 macro
defined by

define(Sequence-package,
$1-foo == ... $2 ...
$1-bar == ... $2 ... )

Now the call
Sequence-package(list,[])
produces the Joy definitions
list-foo == ... [] ...
list-bar == ... [] ...

and the call
Sequence-package(string,"")
produces the Joy definitions
string-foo == ... "" ...
string-bar == ... "" ...

This is worth it only there are more than just two, foo and bar,
and only if they are sufficiently large. Of course one can have
more than two parameters in packages, and M4 would allow one macro
to call others in its body, and even have macros as parameters.
That is where the fun starts. I do not know of a really good
book that deals with the theory of macro expansion. But this
might give you an idea of the sort of things that may be worth
exploring for Joy.

I'll also use this opportunity to answer a question that arose
earlier. Somebody asked about the difference between text macros
and syntax macros. A text macro preprocessor can be used for any
text - letters to mum, Fortran, Joy, ... M4 and the C preprocessor
fall into this class. The result of a call will be some text,
and if that happens to be an illegal program, the compiler will
complain. On the other hand a syntax macro preprocessor would
check the syntax of a macro at the point of definition of the macro.
So such a preprocessor is specific to a particular programming
language. For example M4 will accept the definition
define(echochar, BEGIM reed(ch], wryte(ch) EMMD )
but a Pascal preprocessor would complain angrily.

For the time being I would be content with M4 style text macro
preprocessing - mainly to investigate whether a Joy-specific
syntax macro preprocessor would be worth having.

Hope this helps
Manfred

Louis Madon — 2000-09-07 05:48:36

phimvt@... wrote:
> On Date: Tue, 05 Sep 2000 17:41:50 +1000
> Louis Madon <madonl@...> wrote
>
> >> It may be possible to enter things into the symbol table after
> >> some preliminary (="compile time") processing, but I do not yet
> >> see through all this too clearly. Maybe somebody will want to
> >> have a look at using M4 in detail. _If_ it turns out useful,
> >> one should investigate a taylor-made preprocessor for Joy.
> >
> >So if I understand, your key point is that macros are needed for
> >efficiency reasons (you do not seem to be claiming that macros
> increase
> >expressiveness for example). Specifically, macros would be useful in
> >non-optimising compilers as a way of minimising runtime overhead.
> >Conversely, in an optimising Joy compiler that finds and evaluates
> >invariants at compile time they would serve no useful purpose. Does
> this
> >seem right, or did I miss something?
> >
> Generally my concern is not all that much about machine efficiency,
> but of course I grant that some people emphasise it for good reasons
> in their applications. Inlining is just one instance, and a
> preprocessor
> would save some run time. In my prototype the Joy version square would
> need 3 calls (square, dup, *), and the M4 version Square would need
> 2 calls (dup *). That may be a worthwhile saving.

I agree that the reference implementation of Joy could benefit,
efficiency-wise, from something like m4. In the presence of an optimiser
(esp. partial evaluation) I don't see such benefits, so I was wondering
what else macros in Joy may be good for ...

> But macros can be used for many other things. I see Joy syntax as
> an essential part of the language (more than in most other languages),
> so I do not want to use M4 macros to change the syntax. But one
> could use it for many other purposes which deserve investigation.
> One of them is for packages. Suppose you need almost identical
> sets of definitions for handling strings and list (which collectively
> I call sequences. But for strings and lists you would need to use
> the empty string and the empty list in the two sets of definitions.
> Most other things are the same because things like cons, first,
> rest, concat .. are sufficiently polymorphic. Consider the M4 macro
> defined by
>
> define(Sequence-package,
> $1-foo == ... $2 ...
> $1-bar == ... $2 ... )
>
> Now the call
> Sequence-package(list,[])
> produces the Joy definitions
> list-foo == ... [] ...
> list-bar == ... [] ...
>
> and the call
> Sequence-package(string,"")
> produces the Joy definitions
> string-foo == ... "" ...
> string-bar == ... "" ...

Ah yes, generating new top-level definitions programatically. However,
if the language has a type system that supports overloading you
shouldn't need to do that. Actually, the specific example above doesn't
even need a type system:

foo == ... [] ...
bar == ... [] ...

[] is the empty list as usual. This works if strings are treated as
lists of chars and therefore "" and [] are the same thing. More
generally though, I see your point as:

foo == ... newOfTypeA ...
foo == ... newOfTypeB ...

ie. identical code except that we need to create instances of different
types within the function body depending on the context. All you need
then is polymorphism on return type:

new (TypeA) == ... create a new instance of type A
new (TypeB) == ... create a new instance of type B

and then do

foo == ... new ...

Moreover, we can add additional overloads on 'new' at any time and foo
will automatically handle them without any changes to itself.

(BTW, Haskell also treats strings as lists of char and it also has
polymorphism on return type).


> This is worth it only there are more than just two, foo and bar,
> and only if they are sufficiently large. Of course one can have
> more than two parameters in packages, and M4 would allow one macro
> to call others in its body, and even have macros as parameters.
> That is where the fun starts. I do not know of a really good
> book that deals with the theory of macro expansion. But this
> might give you an idea of the sort of things that may be worth
> exploring for Joy.

Maybe I'm just being too closed-minded but I still feel macros are an
unnneccesary complication. I'll summarise as follows:

Points for (so far):

1 - They improve runtime efficiency (eg. by inlining)
2 - They let you create top-level definitions programmatically

My counterpoints:

1 - A good optimiser can do better without requiring language extensions
2 - A type system can eliminate the need for (2) above

I'm not 100% sure about the validity of my counterpoint 2, but certainly
your example above doesn't present a problem.


Can you think of any other reasons for having macros?


--
Louis.