Subject: [clisp-list] New, from-scratch implementation of backquote.

From: "Hoehle, Joerg-Cyril" <[email-masked]>
Date: Fri Apr 11 08:09:05 2003

Hi,

I found the following thread from 1999 quite intriguing:

Christopher R. Barry in cll on: quasiquote reference implementation
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=87vh8hk0dj.fsf%402xtreme.net&rnum=8
(defmacro defclass* (name supers slots &rest options)
  `(macrolet ((do-it ()
       `(defclass ,,name ,,supers ,,slots ,,@options)))
     (do-it)))
Now you can do something like:
  USER(15): (loop for i from 1 to 3
    collect (defclass* (intern (format nil "FOO-~D" i)) () ()))
  (#<STANDARD-CLASS FOO-1> #<STANDARD-CLASS FOO-2> #<STANDARD-CLASS FOO-3>)

In CLISP-2.30, this gives
*** - EVAL: variable I has no value

For reasons I don't yet understand (I'm not even sure one can expect the do-it trick to work in all kinds of macroexpansion).
A simple case works:
> (defclass* (intern "FOO-1") () ())
#<STANDARD-CLASS FOO-1>

BTW, the above thread is the one (from 1999) were I suggested a programmable interface to backquote: what you implemented now for CLISP!

Regards,
	Jorg Hohle.

---

From: Kaz Kylheku <[email-masked]>
Date: Fri Apr 11 12:24:08 2003

On Fri, 11 Apr 2003, Hoehle, Joerg-Cyril wrote:

> Date: Fri, 11 Apr 2003 13:22:31 +0200
> From: "Hoehle, Joerg-Cyril" <[email-masked]>
> To: [email-masked]
> Cc: [email-masked]
> Subject: New, from-scratch implementation of backquote.
> 
> Hi,
> 

Interesting; basically this uses macrolet solely to obtain an extra
level of macroexpansion, so that the name, supers, slots and options
are all evaluated.

However, because defclass is a macro, this relies on evaluating the
forms at macroexpand time, so that their evaluated values can be stuck
into the defclass form itself.

So the expressions can only refer to functions and variables that
are available at that time.

> *** - EVAL: variable I has no value

This is right because the lexcal variable i does not exist at the
macrolet expansion time.  You see the (defclass* ...) construct can be
(and is) expanded before the (loop ...) is executed.

A macrolet construct cannot give you a different macroexpansion each
time you evaluate it; it's just a scoping mechanism for local macros,
not a dynamic macro mechanism. Local macros give you a way to hook into
the system's code walker, but that walk happens just once.

> > (defclass* (intern "FOO-1") () ())

Yeah, but this is at the top level and as such doesn't rely on any
lexical variables. 

> BTW, the above thread is the one (from 1999) were I suggested a programmable interface to backquote: what you implemented now for CLISP!

I don't think that will help, because it's not a backquote problem.

To solve this problem you need the help of EVAL. Your loop must
actually construct the *source code* of a DEFCLASS construct into which
it has inserted all the right pieces, and then EVAL that source code
so that its effect takes place.

Example:

 (loop for i from 1 to 3
       collect `(defclass ,(intern (format nil "FOO-~D" i)) () ())
         into defclasses
       finally (eval `(progn ,@defclasses)))

We dynamically a list, a piece of data, which looks like:

  ((defclass FOO-1 ...)
   (defclass FOO-2 ...)
   (defclass FOO-3 ...))

Then we stick a PROGN in front and EVAL it as code, causing the classes
to be defined. The loop can be written so that it generates a
completely different list each time, using a different range of
integers and different base name for the symbol. Each time you call it,
it will generate some data and then evaluate it as code.  Macros cannot
do that; they are expanded at compile time to produce their
substitution and then go away. 
   
I will try the original trick with the new backquote, but I'm almost
certain that it won't work.

I should mention that the support for ,,@ actually does work in the old
backquote. The implementation notes are misleading.

---

From: Todd Sabin <[email-masked]>
Date: Fri Apr 11 13:14:08 2003

> level of macroexpansion, so that the name, supers, slots and options

Which maybe means that you probably don't want a macro, but a function
instead...

> [... lucid comments elided ...]
> 

Defining defclass* as a function avoids the need for eval:

(defun defclass* (name supers slots &rest options)
  (funcall (lambda () `(defclass ,name ,supers ,slots ,@options))))

(loop for i from 1 to 3
   collect (defclass* ( (intern (format nil "FOO-~D" i)) () ())

---

From: Todd Sabin <[email-masked]>
Date: Fri Apr 11 13:23:12 2003

Perhaps it's obvious, but the last message was unfinished; I never
noticed how close C-c C-c is to C-x C-x before :\

Anyway, the code I sent doesn't do what I thought it did, so just ignore
me for now... :)

Todd

---

From: Kaz Kylheku <[email-masked]>
Date: Fri Apr 11 13:36:06 2003

> Anyway, the code I sent doesn't do what I thought it did, so just ignore

Yep. It returns the list (defclass ...) which still needs to be EVAL-ed. 

You can't fix this, because you can't get around the fact that defclass
is a compile-time construct.

To define a new class at run-time, you need to construct a new instance
of the defclass syntax, and to activate it, you must either EVAL it,
or put it in a lambda body and COMPILE it, and then funcall the
resulting object.

The other alternative would be to use the underlying ENSURE-CLASS
function, but that is not portable, (except where the MOP is
implemented)?

So we can repair your function by moving the backquote out to the whole
lambda, and then by using the COMPILE function---which is probably kind
of a waste of cycles just to run defclass once. ;)

;; original recipe
(defun defclass* (name supers slots &rest options)
  (funcall (lambda () 
             `(defclass ,name ,supers ,slots ,@options))))

;; extra crispy
(defun defclass* (name supers slots &rest options)
  (funcall (compile nil 
                    `(lambda () 
                       (defclass ,name ,supers ,slots ,@options)))))

;; calorie reduced
(defun defclass* (name supers slots &rest options)
  (eval `(defclass ,name ,supers ,slots ,@options)))

---

From: Todd Sabin <[email-masked]>
Date: Fri Apr 11 13:57:07 2003

> or put it in a lambda body and COMPILE it, and then funcall the

Right, I was just about to send corrected code identical to the extra
crispy below...

> of a waste of cycles just to run defclass once. ;)

That's interesting.  I guess I've been prejudiced against eval so much
by all the "you almost never really need eval" stuff around that I
always avoid it if there's an alternative.  But this does seem to be a
perfectly good use for it.  Thanks.

>   (eval `(defclass ,name ,supers ,slots ,@options)))

I'd still like to see a real-world application of the original
macrolet examples, if anyone knows one.  When I first read that stuff
I had the feeling of a light bulb going on somewhere, but I haven't
figured out where it is or what it's illuminating, yet...

Todd

---

From: Kaz Kylheku <[email-masked]>
Date: Thu Apr 24 10:00:07 2003

On Cygwin, the /dev/tty file exists, but the directory /dev does not. WE 
get this:

  [1]> (with-open-file (x "/dev/tty"))

  *** - nonexistent directory: #P"/dev/"

Second-guessing the operating system's path component resolution
strategy not only wastes cycles, but leads to incorrect guesses when
the operating system recognizes special cases.

Can't CLISP just take the pathname string and pass it to the open()
system call?