Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!bb3.andrew.cmu.edu!newsfeed.pitt.edu!godot.cc.duq.edu!newsgate.duke.edu!news.mathworks.com!newsfeed.internetmci.com!in3.uu.net!news.biu.ac.il!discus.technion.ac.il!news!qobi
From: qobi@eesun.technion.ac.il (Jeffrey Mark Siskind)
Subject: Re: Scheme or ML? (was Re: multiple values)
Reply-To: Qobi@EE.Technion.AC.IL
Organization: Technion, Israel Institute of Technology
Date: Mon, 12 Aug 1996 06:26:52 GMT
Message-ID: <QOBI.96Aug12092652@eesun.technion.ac.il>
In-Reply-To: wilson@cs.utexas.edu's message of 7 Aug 1996 12:49:31 -0500
X-Nntp-Posting-Host: eesun.technion.ac.il
References: <qijn30oeaoq.fsf@lambda.ai.mit.edu> <32061C42.67A1@cs.cmu.edu>
	<4u7gus$i2r@roar.cs.utexas.edu> <3208B9B3.2BEE@cs.cmu.edu>
	<4uakvb$5jd@jive.cs.utexas.edu>
Sender: news@discus.technion.ac.il (News system)
Lines: 143

   Exactly right.  That would be the wrong thing to do.  It's exactly the
   problem with Lisp macros---you're writing surface syntax transformations and
   the surface syntax doesn't respect little things like scope.

   This is what Scheme gets right, and why Scheme's lexically-scoped
   ("hygienic") macros are so interesting.

One thing that people often forget about when discussing macros is that
procedural macros are often used not just to extend the syntax of Lisp/Scheme
with little special forms like DO, LOOP, etc. but rather to implement whole new
sublanguages. People use macros to implement Prolog, forward chaining rule
systems, grammars for natural language, languages to write expressions in
various logics, etc. The bodies of such macros are not Lisp/Scheme code. They
are not intended to be interpreted by the same semantics as Lisp/Scheme
expressions. For such purposes, any macro system that attempts to integrate
the semantics of the host programming language into the macro expansion
process actually gets in the way. This includes variable scoping issues, type
inference, and the like.

One of the nice things about Lisp/Scheme is that the surface S expression
parser is so language neutral. This comes in very handy and allows a useful
form of macro reuse: Most languages can use some form of IF, independent of
whether if it Prolog, forward chaining rule systems, grammars for natural
language, various logics, etc. In Lisp/Scheme it is natural to represent this
construct as (IF <antecedent> <consequent> <alternate>) in any embedded
language. Now besides the trivial benefits like that my editor now nows how to
indent IF constructs in all of these embedded languages, and aesthetically,
these IF constructs, have a uniform appearance, there is an additional
benefit: if I write my macro correctly, and design my embedded language
correctly, it can inherit all of the macros from the base Lisp/Scheme
environment that expand into IF. Things like COND, WHEN, UNLESS, CASE, etc.

I'd also like to emphasize the point of the syntactic uniformity of
S expressions and their use in sublanguages that are implemented via macros.
Lisp/Scheme programmers have been writing things like the following:


(prove
 (every x
        (implies (and (integer x) (> x 2) (even x))
                 (some y (some z (and (prime y) (prime z) (= x (+ y z))))))))

for almost 40 years. Or:

(parse
 ((constrain (=> s (seq np vp)) (and (= (person np) (person vp))
                                     (= (number np) (number vp))))
  (constrain (=> vp (seq v np)) (and (= (person vp) (person v))
                                     (= (number vp) (number v))))
  ...)
 (colorless green ideas sleep furiously))

The important thing about these example is that many embedded languages inherit
syntactic notions from the host language: Logic and Lisp/Scheme share notions
like EVERY, AND, INTEGER, >, EVEN, SOME, =, and +. Unification grammars and
Lisp/Scheme share notions like AND, =, and function application. Nothing about
this is particular to prefix syntax. One could imagine an infix syntax that
allowed one to use host-language syntactic constructs to express Goldbach's
conjecture:

prove(every(x,(integer(x)&x>2&even(x))->
              some(y,some(z,prime(y)&prime(z)&x=y+z))));

or a grammar:

parse([s-->np,vp{person(np)=person(vp)&number(np)=number(vp)},
       vp-->v,np{person(vp)=person(v)&number(vp)=number(v)},
       ...],
      "colorless green ideas sleep furiously");

But the important point is that most non-Lisp/Scheme implementations do not
let you use the host language syntax in such a fashion. Instead they force you
to write things like:

prove(['every', 'x'
                 ['implies', ['and', ['integer', 'x'],
                                     ['>', 'x', '2'],
                                     ['even', 'x']]
                             ['some', 'y',
                              ['some', 'z',
                                ['and', ['prime', 'y'],
                                        ['prime', 'z'],
                                        ['=', 'x', ['+', 'y', 'z']]]]]]]);

or:

parse([['constrain', ['=>', 's', ['seq', 'np', 'vp']],
                     ['and', ['=', ['person', 'np'], ['person', 'vp']],
                             ['=', ['number', 'np'], ['number', 'vp']]]],
       ['constrain', ['=>', 'vp', ['seq', 'v', 'np']],
                     ['and', ['=', ['person', 'vp'], ['person', 'v']],
                             ['=', ['number', 'vp'], ['number', 'v']]]]
        ...],
      "colorless green ideas sleep furiously");

This is very unnatural and ugly. It hinders proper writing and understanding
of programs. You might think that this is just a parody. That real programs
don't do this very often and that real programs don't look this unnatrual and
ugly. Well, here is some ML code that I have excerpted from the TIL Leroy
benchmark:

val Group_rules = [
  (1, (1, (Term("*", [Term("U",[]), Var 1]), Var 1))),
  (2, (1, (Term("*", [Term("I",[Var 1]), Var 1]), Term("U",[])))),
  (3, (3, (Term("*", [Term("*", [Var 1, Var 2]), Var 3]),
           Term("*", [Var 1, Term("*", [Var 2, Var 3])]))))];

val Geom_rules = [
 (1,(1,(Term ("*",[(Term ("U",[])), (Var 1)]),(Var 1)))),
 (2,(1,(Term ("*",[(Term ("I",[(Var 1)])), (Var 1)]),(Term ("U",[]))))),
 (3,(3,(Term ("*",[(Term ("*",[(Var 1), (Var 2)])), (Var 3)]),
  (Term ("*",[(Var 1), (Term ("*",[(Var 2), (Var 3)]))]))))),
 (4,(0,(Term ("*",[(Term ("A",[])), (Term ("B",[]))]),
  (Term ("*",[(Term ("B",[])), (Term ("A",[]))]))))),
 (5,(0,(Term ("*",[(Term ("C",[])), (Term ("C",[]))]),(Term ("U",[]))))),
 (6,(0,
  (Term
   ("*",
    [(Term ("C",[])),
     (Term ("*",[(Term ("A",[])), (Term ("I",[(Term ("C",[]))]))]))]),
  (Term ("I",[(Term ("A",[]))]))))),
 (7,(0,
  (Term
   ("*",
    [(Term ("C",[])),
     (Term ("*",[(Term ("B",[])), (Term ("I",[(Term ("C",[]))]))]))]),
  (Term ("B",[])))))
];

Now I haven't spent the time tying to decipher this code. It appears that it
expresses some rules for an equational theorem prover. But this is
unintelligible.

Now the ML community can spend as much time as it wants arguing about abstract
syntax, suface syntax, and the like. And maybe they will eventually figure out
a way to write more intelligible versions of the above. But then they will
simply be reinventing the something that the Lisp/Scheme community has been
doing for almost 40 years.

    Jeff (home page http://tochna.technion.ac.il/~qobi)
-- 

    Jeff (home page http://tochna.technion.ac.il/~qobi)
