From: Niclas Lars�n
Subject: Implementing COND
Date: 
Message-ID: <3ae737f6@news.mdh.se>
Hi, I'm trying to implement the cond-macro in LISP (implementing a
interpreter)
I have a problem with the arguments to cond being evaluated
before calling cond... it's a special-form as "if", is there some keyword to
express this?
(or mabe lambda-keyword - is argument evalueted before call to
function/lambda-exp. or inside function?)
I don't want to call cond with a qouted list, it ain't correct...
Here is my code wich requires quoted arguments/list to cond and incorrectly
returns answer in a list...

(defun cond (x)
    (if (car (car x)) (cdr (car x)) (cond (cdr x))))

 /Niclas

From: Barry Margolin
Subject: Re: Implementing COND
Date: 
Message-ID: <NFHF6.61$Lt5.1015@burlma1-snr2>
In article <········@news.mdh.se>,
Niclas Lars�n <·············@swipnet.se> wrote:
>Hi, I'm trying to implement the cond-macro in LISP (implementing a
>interpreter)
>I have a problem with the arguments to cond being evaluated
>before calling cond... it's a special-form as "if", is there some keyword to
>express this?
>(or mabe lambda-keyword - is argument evalueted before call to
>function/lambda-exp. or inside function?)
>I don't want to call cond with a qouted list, it ain't correct...
>Here is my code wich requires quoted arguments/list to cond and incorrectly
>returns answer in a list...
>
>(defun cond (x)
>    (if (car (car x)) (cdr (car x)) (cond (cdr x))))

You have to use defmacro, not defun, to define a macro:

(defmacro cond (&rest clauses)
  (if (null clauses)
      `nil
      (let* ((first-clause (first clauses))
             (test (first first-clause))
             (result (rest first-clause))
             (rest-clauses (rest clauses)))
	`(if ,test
	     (progn ,@result)
	     (cond ,@rest-clauses)))))

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Niclas Lars�n
Subject: Re: Implementing COND
Date: 
Message-ID: <3ae74c4f@news.mdh.se>
Does anyone know how "defmacro" could be implemented in LISP-code,
or do I have to implement it as a primitive (...in interpreter and in native
code)?
From: Michael Parker
Subject: Re: Implementing COND
Date: 
Message-ID: <F3739812F87F99EC.CFDFD12A6F429D92.CA537CC20B0A703E@lp.airnews.net>
"Niclas Lars�n" wrote:
> 
> Does anyone know how "defmacro" could be implemented in LISP-code,
> or do I have to implement it as a primitive (...in interpreter and in native
> code)?

Depends.  It's your interpreter, after all.  This is one of those design
decisions that make for good learning experiences :-)

One way to do it is just putprop the macro to a 'macro property on the
'cond symbol.  Your eval function would then check for this property if
the 'expr and 'subr properties weren't present.  You can also do macro
expansion at read time, which is more efficient unless you cache your
expansions.

Or if you're implementing an old-fashioned dynamically-scoped lisp,
you can implement it as a fexpr or nlambda, and pass in the arguments
unevaluated, in which case the implementation for cond would be quite
similar to your first attempt (the arguments would act like they were
quoted).

have fun!
From: Barry Margolin
Subject: Re: Implementing COND
Date: 
Message-ID: <%%JF6.68$Lt5.749@burlma1-snr2>
In article <··················································@lp.airnews.net>,
Michael Parker  <·······@pdq.net> wrote:
>"Niclas Lars�n" wrote:
>> 
>> Does anyone know how "defmacro" could be implemented in LISP-code,
>> or do I have to implement it as a primitive (...in interpreter and in native
>> code)?
>
>Depends.  It's your interpreter, after all.  This is one of those design
>decisions that make for good learning experiences :-)
>
>One way to do it is just putprop the macro to a 'macro property on the
>'cond symbol.  Your eval function would then check for this property if
>the 'expr and 'subr properties weren't present.  You can also do macro
>expansion at read time, which is more efficient unless you cache your
>expansions.

Doing macro expansion at read time would make it difficult to handle
recursive macros, like the recursive definition I gave of COND.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Michael Parker
Subject: Re: Implementing COND
Date: 
Message-ID: <30B689DDA2B5674C.2934AA330F626EE6.21E1FE043E1B3B1A@lp.airnews.net>
> Doing macro expansion at read time would make it difficult to handle
> recursive macros, like the recursive definition I gave of COND.

Not necessarily -- the recursive cond was protected inside a backquote.

You can't treat cond like a readmacro though, I was thinking more of
(print (eval (macroexpand (read)))), which isn't actually read-time,
but at least doesn't involve evaluating macros inside eval.
From: Tim Bradshaw
Subject: Re: Implementing COND
Date: 
Message-ID: <nkjae52aduk.fsf@tfeb.org>
Michael Parker <·······@pdq.net> writes:

> Not necessarily -- the recursive cond was protected inside a backquote.
> 
> You can't treat cond like a readmacro though, I was thinking more of
> (print (eval (macroexpand (read)))), which isn't actually read-time,
> but at least doesn't involve evaluating macros inside eval.

Yes, it does, because MACROEXPAND doesn't do what EVAL must do.  You
probably know this, but it's often misunderstood, hence this article!

What MACROEXPAND does is expand a form it's not a `macro form', which
means it's neither a symbol macro nor a cons whose car names a macro.
It *doesn't* expand any macros inside a compound form.  For instance, given

(defmacro if-not (test-not then &optional else)
  `(if ,test-not ,else ,then))

Then 

> (macroexpand '(if-not (if-not x y z) 1 2))
(if (if-not x y z) 2 1)
t

EVAL has to walk over the code and know which bits should be eligible
for macroexpansion - that's why writing a `proper' version of
MACROEXPAND is hard, since you need a code walker.

--tim
From: Kent M Pitman
Subject: Re: Implementing COND
Date: 
Message-ID: <sfwd79y8urk.fsf@world.std.com>
Tim Bradshaw <···@tfeb.org> writes:

> Michael Parker <·······@pdq.net> writes:
> 
> > You can't treat cond like a readmacro though, I was thinking more of
> > (print (eval (macroexpand (read)))), which isn't actually read-time,
> > but at least doesn't involve evaluating macros inside eval.
> 
> Yes, it does, because MACROEXPAND doesn't do what EVAL must do.  You
> probably know this, but it's often misunderstood, hence this article!
> 
> What MACROEXPAND does is expand a form it's not a `macro form', which
> means it's neither a symbol macro nor a cons whose car names a macro.
> It *doesn't* expand any macros inside a compound form.

Right. MACLISP had (in the compiler only) MACRO-EXPAND (pronounced
"macro dash expand") which did this, as distinct from MACROEXPAND (which,
as in CL, did not).  The Lisp Machine's Zetalisp has MACROEXPAND-ALL, I
believe (not sure of the package, but probably COMPILER or SI).
Expanding all macros is really quite handy, but requires a codewalker and
is quite complicated.  In fact, though, I'd bet there is no implementation
which doesn't have this capability hidden away in it (in part, because of
the requirements of minimal file compilation for COMPILE-FILE), so it's
really a darned shame there isn't an exposed operation that does this.

> For instance, given
> 
> (defmacro if-not (test-not then &optional else)
>   `(if ,test-not ,else ,then))
> 
> Then 
> 
> > (macroexpand '(if-not (if-not x y z) 1 2))
> (if (if-not x y z) 2 1)
> t
> 
> EVAL has to walk over the code and know which bits should be eligible
> for macroexpansion - that's why writing a `proper' version of
> MACROEXPAND is hard, since you need a code walker.

What Tim says is right here.

But what Michael might have thought he was asking for, even though it
happens not to be evailable, is a reasonable thing to wish were there.
From: Fred Gilham
Subject: Macroexpand-All (was Re: Implementing COND)
Date: 
Message-ID: <u7g0eu1fhv.fsf_-_@snapdragon.csl.sri.com>
Kent Pitman writes:

> Right. MACLISP had (in the compiler only) MACRO-EXPAND (pronounced
> "macro dash expand") which did this, as distinct from MACROEXPAND
> (which, as in CL, did not).  The Lisp Machine's Zetalisp has
> MACROEXPAND-ALL, I believe (not sure of the package, but probably
> COMPILER or SI). Expanding all macros is really quite handy, but
> requires a codewalker and is quite complicated.  In fact, though,
> I'd bet there is no implementation which doesn't have this
> capability hidden away in it (in part, because of the requirements
> of minimal file compilation for COMPILE-FILE), so it's really a
> darned shame there isn't an exposed operation that does this.


In LISP POINTERS, Vol. VI, No. 1, there's an article by Richard Waters
called "Macroexpand-All: An Example of a Simple Lisp Code Walker"
which does this.  It requires an implementation-specific hack to give
access to the macro environment.

-- 
Fred Gilham                                   ······@csl.sri.com
Jordan Hubbard: We have a crash bug.  It needs to be fixed. We DO NOT
need to know how to print 3000 spaces in 11 different languages! :-)
Daniel Sobral: I concur. But if anyone wants to do it with loader,
: 3kbl 3000 0 do bl emit loop ; 3kbl will do the trick.
From: Tim Bradshaw
Subject: Re: Implementing COND
Date: 
Message-ID: <ey3elugv7j3.fsf@cley.com>
* Niclas Lars�n wrote:
> Does anyone know how "defmacro" could be implemented in LISP-code,
> or do I have to implement it as a primitive (...in interpreter and in native
> code)?

I think it has to be fairly primitive, in the sense that macros need
support in the evaluator.  Basically there needs to be a
place where, when a compound form is being evaluated, the evaluator
looks at the car of the form, and switches on


        a special operator - idiosyncratic rule per operator
        a macro - find macro function, call it with the form, evaluate
                the resulting form
        a function - evaluate arguments, apply function to result.


OTOH, given an evaluator which does not understand macros it should be
quite easy to write one which does - the only really fiddly bit is
dealing with special forms right - you need to know which subforms
might get evaluated.

--tim

(I guess I should qualify `quite easy' - it's quite easy for a simple
lisp dialect, if you want to do a full CL evaluator it would be quite
a lot harder.)
From: Lars Lundback
Subject: Re: Implementing COND
Date: 
Message-ID: <3ae7e24c.1454807906@news.ericsson.se>
On Thu, 26 Apr 2001 00:17:08 +0200, "Niclas Lars�n"
<·············@swipnet.se> wrote:

>Does anyone know how "defmacro" could be implemented in LISP-code,
>or do I have to implement it as a primitive (...in interpreter and in native
>code)?

First, there are certainly people in this newsgroup that could write
whole books on this and other implementation issues, if there were
visible gains to be made ... 

The best way is probably to study the code, both Lisp and low-level,
(C and/or assembler), that is available for several Lisp
implementations. You merely say "Lisp", but which kind of Lisp do you
have in mind? I hope you mean Common Lisp - if so, study the sources
(Corman CL, CMUCL, Clisp, etc) along with the CL HyperSpec chapter 2
on syntax, especially backquote. 

The main obstacle here is the wide span between "interpreter" and
"native code". One can device several steps in between,where the Lisp
source is processed into internal objects which are invisible to you
and used differently, depending on how "native" you want to go.

Last, I too consider implementing a (part of) Lisp as a very good
learning experience.  I did in fact comment a while ago that too few
seem to undertake it, and would like to hear your motives for doing
it?

Regards, Lars
From: Markus B. Kr�ger
Subject: Re: Implementing COND
Date: 
Message-ID: <du3eluf90w0.fsf@proto.pvv.ntnu.no>
Barry Margolin <······@genuity.net> writes:

> In article <········@news.mdh.se>,
> Niclas Lars�n <·············@swipnet.se> wrote:
> >Hi, I'm trying to implement the cond-macro in LISP (implementing a
> >interpreter)
> >[...]
> 
> You have to use defmacro, not defun, to define a macro:
> 
> (defmacro cond (&rest clauses)
>   (if (null clauses)
>       `nil
>       (let* ((first-clause (first clauses))
>              (test (first first-clause))
>              (result (rest first-clause))
>              (rest-clauses (rest clauses)))
> 	`(if ,test
> 	     (progn ,@result)
> 	     (cond ,@rest-clauses)))))

The definition above does not handle the case where there are no forms
after the test form; in this case, the primary value of the test form
should be returned, according to the HyperSpec.  Also, I find
destructuring-bind easier to read than let*, but this is of course a
matter of taste.  Here's my go at it:

(defmacro cond (&rest clauses)
  (if (null clauses)
      `nil
      (destructuring-bind ((test &rest result) &rest rest-clauses) 
                          clauses
        (if (null result)
            `(or (nth-value 0 ,test)
                 (cond ,@rest-clauses))
            `(if ,test
                 (progn ,@result)
                 (cond ,@rest-clauses))))))

-- 
 ,-------------------  Markus Bjartveit Kr�ger  ---------------------.
'                                                                     `
` E-mail: ·······@pvv.org           WWW: http://www.pvv.org/~markusk/ '
 )-------------------------------------------------------------------(
From: Barry Margolin
Subject: Re: Implementing COND
Date: 
Message-ID: <MuZF6.8$EE6.548@burlma1-snr2>
In article <···············@proto.pvv.ntnu.no>,
Markus B. Kr�ger <·······@pvv.org> wrote:
>The definition above does not handle the case where there are no forms
>after the test form; in this case, the primary value of the test form
>should be returned, according to the HyperSpec.  Also, I find
>destructuring-bind easier to read than let*, but this is of course a
>matter of taste.  Here's my go at it:

I was deliberately keeping it simple.  I was going to use
DESTRUCTURNG-BIND, but since the OP didn't know about macros, I doubted he
would recognize DESTRUCTURING-BIND.  And since he's just implementing a toy
Lisp for what seemed like an academic exercise, I didn't think it was
necessary to deal with that obscure feature of COND's syntax (the version
of COND that he tried to write himself didn't handle it, either).

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Janis Dzerins
Subject: Re: Implementing COND
Date: 
Message-ID: <87g0eu4sha.fsf@asaka.latnet.lv>
A little bit about style...

·······@pvv.org (Markus B. Kr�ger) writes:

> (defmacro cond (&rest clauses)
>   (if (null clauses)
>       `nil

I wonder about this backquote before nil -- I assume it's there just
to let the reader know that it is something returned as a
macroexpansion. Or is there any other reasons behind that? (But then
-- why backquote and not quote or no quote at all?)

>       (destructuring-bind ((test &rest result) &rest rest-clauses) 
>                           clauses
>         (if (null result)
>             `(or (nth-value 0 ,test)

I'd write just `(or (values ,test)).

>                  (cond ,@rest-clauses))
>             `(if ,test
>                  (progn ,@result)
>                  (cond ,@rest-clauses))))))

-- 
Janis Dzerins

  If million people say a stupid thing it's still a stupid thing.
From: Markus B. Kr�ger
Subject: Re: Implementing COND
Date: 
Message-ID: <du3g0euy670.fsf@proto.pvv.ntnu.no>
Janis Dzerins <·····@latnet.lv> writes:

> A little bit about style...
> 
> ·······@pvv.org (Markus B. Kr�ger) writes:
> 
> > (defmacro cond (&rest clauses)
> >   (if (null clauses)
> >       `nil
> 
> I wonder about this backquote before nil -- I assume it's there just
> to let the reader know that it is something returned as a
> macroexpansion. Or is there any other reasons behind that? (But then
> -- why backquote and not quote or no quote at all?)

That part of the macro was copied verbatim from Barry's original; I
don't know if there are other reasons for using backquote than the one
you mentioned.

> >       (destructuring-bind ((test &rest result) &rest rest-clauses) 
> >                           clauses
> >         (if (null result)
> >             `(or (nth-value 0 ,test)
> 
> I'd write just `(or (values ,test)).

I wasn't aware of this idiom for returning only one value.  Thanks!

-- 
 ,-------------------  Markus Bjartveit Kr�ger  ---------------------.
'                                                                     `
` E-mail: ·······@pvv.org           WWW: http://www.pvv.org/~markusk/ '
 )-------------------------------------------------------------------(
From: Kent M Pitman
Subject: Re: Implementing COND
Date: 
Message-ID: <sfwelue8v69.fsf@world.std.com>
·······@pvv.org (Markus B. Kr�ger) writes:

> Janis Dzerins <·····@latnet.lv> writes:
> 
> > A little bit about style...
> > 
> > ·······@pvv.org (Markus B. Kr�ger) writes:
> > 
> > > (defmacro cond (&rest clauses)
> > >   (if (null clauses)
> > >       `nil
> > 
> > I wonder about this backquote before nil -- I assume it's there just
> > to let the reader know that it is something returned as a
> > macroexpansion. Or is there any other reasons behind that? (But then
> > -- why backquote and not quote or no quote at all?)
> 
> That part of the macro was copied verbatim from Barry's original; I
> don't know if there are other reasons for using backquote than the one
> you mentioned.

I prefer to see backquote before such things, even t and nil and numbers,
for several reasons.

Backquote helps visually identify macro expansion results.

Any quotation helps remind people of how many levels of evaluation
need occur before the symbolic token takes on meaning.  In Brian
Smith's 3lisp, there's a notion of something called a numeral that
evaluates to a number, and you have to evaluate '3 to get the number
3, for example.

Backquote is good "parallel construction" with the other elements of
the definition, since they use backquote.  (The same concept as would
apply in a natural language.)  It helps you identify the primary parallel
paths of execution that evaluation might take through a definition,
whether ultimately combining like tributaries to a river or temporally
separated paths chosen on other invocations.
From: Barry Margolin
Subject: Re: Implementing COND
Date: 
Message-ID: <mFhG6.54$EE6.1521@burlma1-snr2>
In article <···············@world.std.com>,
Kent M Pitman  <······@world.std.com> wrote:
>·······@pvv.org (Markus B. Kr�ger) writes:
>
>> Janis Dzerins <·····@latnet.lv> writes:
>> 
>> > A little bit about style...
>> > 
>> > ·······@pvv.org (Markus B. Kr�ger) writes:
>> > 
>> > > (defmacro cond (&rest clauses)
>> > >   (if (null clauses)
>> > >       `nil
>> > 
>> > I wonder about this backquote before nil -- I assume it's there just
>> > to let the reader know that it is something returned as a
>> > macroexpansion. Or is there any other reasons behind that? (But then
>> > -- why backquote and not quote or no quote at all?)
>> 
>> That part of the macro was copied verbatim from Barry's original; I
>> don't know if there are other reasons for using backquote than the one
>> you mentioned.
>
>I prefer to see backquote before such things, even t and nil and numbers,
>for several reasons.
>
>Backquote helps visually identify macro expansion results.
>
>Any quotation helps remind people of how many levels of evaluation
>need occur before the symbolic token takes on meaning.  In Brian
>Smith's 3lisp, there's a notion of something called a numeral that
>evaluates to a number, and you have to evaluate '3 to get the number
>3, for example.
>
>Backquote is good "parallel construction" with the other elements of
>the definition, since they use backquote.  (The same concept as would
>apply in a natural language.)  It helps you identify the primary parallel
>paths of execution that evaluation might take through a definition,
>whether ultimately combining like tributaries to a river or temporally
>separated paths chosen on other invocations.

Those were essentially my reasons for putting the backquote there.  I
automatically start backquoting when I'm specifying an expansion, and in
this case it's just happenstance that the expansion is a self-evaluating
object.  There's no need to go back and unquote it -- I expect that just
about every compiler automatically transforms (quote <self-eval>) to
<self-eval> (actually, I really suspect that it's usually the opposite
transformation that takes place, so that later phases of the compiler don't
need to know about self-evaluating objects at all, they just deal with
quoted and non-quoted expressions, but NIL may be a special case exception
to this).

Another thing: while I was writing the macro, I originally had that case
as:

  (if (null clauses)
      `(progn)

and I was debating whether or not to use this or `nil.  I decided to go
with NIL since if I were writing the corresponding IF manually it would
look something like

  (if <condition>
      <consequence>
      nil)

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.