From: Eric Dedieu
Subject: Load-time problem with a macro that defines a macro
Date: 
Message-ID: <1445@dione.imag.fr>
Hello,

I have a problem while using a macro to write another macro: it works well at
compile and eval time, but puzzles me at load time.

The macro I want should be like "defmethod" (in Lucid's flavor system), but
return the name of the created function as *.
(defmethod-flavor toto) creates a macro named "defmethod-toto", and

(defmethod-toto :foo ...) expands as

(Progn (Defmethod (Toto :Foo) ...)
       (Identity (Find-Symbol (String-Append "TOTO" "-" "FOO" "-PRIMARY"))))

so it returns "toto-foo-primary" as * (note: I could have written a simpler
"defmethod2" as a single macro, but "defmethod-toto"-like forms were already
used for "historical" reasons)

When defining a flavor, I put the (defmethod-flavor toto) in an
(eval-when (compile load eval)) form, to define the macro before using it.

Here is a "skinned" piece of code:

;;; In a first file "bug1", the global macro, in a general-utility package
;;; The created function is put in the same package and then exported,
;;; to make things clear.

(in-package 'tools)

(export '(defmethod-flavor))

(defmacro defmethod-flavor (flavor)
  `(export
     (defmacro ,(intern (string-append "DEFMETHOD-" (symbol-name flavor)) 'tools)
         (nom args &body body)
       (list 'progn
             (cons 'flavors::defmethod
                   (cons (list (quote ,flavor) nom)
                         (cons args body)))
             (list 'identity
                   (list 'find-symbol
                         (list 'string-append
                               ,(symbol-name flavor)
                               "-"
                               (symbol-name nom)
                               "-PRIMARY")))))
    'tools))


(provide "bug1")

;;; In a second file, how it is used

(in-package 'user)

(use-package 'flavors)

(require "bug1")
(use-package 'tools)

(defflavor toto
 (a)
 ())

(eval-when (compile load eval)
  (defmethod-flavor toto))

(defmethod-toto :foo (x)
  (+ x a))

[many other "defmethod-toto"s]

;;; end

The result at load time:

> (load "bug2")
;;; Loading binary file "bug2.sbin"
;;; Loading binary file "bug1.sbin"
>>Error: FASLOAD: When compiled, a symbol named DEFMETHOD-TOTO was inherited
             by the "USER" package; but now, no such symbol is accessible.

FASLOAD:
   Required arg 0 (FILENAME): #P"/tmp_mnt/home/sinope/dedieu/kb/lisp/bug2.sbin"
   Keyword arg 1 (VERBOSE): NIL
   Keyword arg 2 (PRINT): NIL
   Keyword arg 3 (IF-DOES-NOT-EXIST): :ERROR
   Keyword arg 4 (DEFAULT-PATHNAME): ".sbin"
   Keyword arg 5 (CODE-AREA): #<Area "Readonly-Non-Pointer-Area" 745EB6>
   Keyword arg 6 (PROCEDURE-AREA): #<Area "Readonly-Pointer-Area" 745EEE>
   Keyword arg 7 (PROCEDURE-CONTENTS-POINTER-AREA): #<Area "Readonly-Pointer-Area" 745EEE>
   Keyword arg 8 (PROCEDURE-CONTENTS-NON-POINTER-AREA): #<Area "Readonly-Non-Pointer-Area" 745EB6>
   Keyword arg 9 (SYMBOL-NAME-AREA): #<Area "Readonly-Non-Pointer-Area" 745EB6>
   Keyword arg 10 (OTHER-AREA): #<Area "Ephemeral Level 0" 984496>
:C  0: INTERN DEFMETHOD-TOTO into the "USER" package.
:A  1: Abort to Lisp Top Level

-> :c
INTERN DEFMETHOD-TOTO into the "USER" package.
>>Error: DEFMETHOD-TOTO cannot be EXPORT'ed in the TOOLS package because it is not 
 accessible there.

EXPORT:
   Required arg 0 (SYMBOLS): (DEFMETHOD-TOTO)
   Optional arg 1 (PACKAGE): #<Package "TOOLS" 98451E>
:C  0: Import symbol Defmethod-Toto into the TOOLS package (and then EXPORT it).
:A  1: Abort to Lisp Top Level

-> :c
Import symbol Defmethod-Toto into the TOOLS package (and then EXPORT it).
#P"/tmp_mnt/home/sinope/dedieu/kb/lisp/bug2.sbin"

And then it does as I want (defmethod-toto is the right macro I described,
but is interned in 'user). I used to continue from the errors while loading the
system, but now I'd rather understand why "defmethod-toto" isn't accessible
at load time...

Thanks,
Eric Dedieu

From: Barry Margolin
Subject: Re: Load-time problem with a macro that defines a macro
Date: 
Message-ID: <1991Aug30.213422.15092@Think.COM>
In article <····@dione.imag.fr> ······@lifia.imag.fr (Eric Dedieu) writes:
>Here is a "skinned" piece of code:
>
>;;; In a first file "bug1", the global macro, in a general-utility package
>;;; The created function is put in the same package and then exported,
>;;; to make things clear.
>
>(in-package 'tools)
>
>(export '(defmethod-flavor))
>
>(defmacro defmethod-flavor (flavor)
>  `(export
>     (defmacro ,(intern (string-append "DEFMETHOD-" (symbol-name flavor)) 'tools)
>         (nom args &body body)
>       (list 'progn
>             (cons 'flavors::defmethod
>                   (cons (list (quote ,flavor) nom)
>                         (cons args body)))
>             (list 'identity
>                   (list 'find-symbol
>                         (list 'string-append
>                               ,(symbol-name flavor)
>                               "-"
>                               (symbol-name nom)
>                               "-PRIMARY")))))
>    'tools))

First of all, it would help alot if you would use backquote in the inner
macro as well as the outer one.  I can't parse the above.

[Lots of scripting deleted to appease inews -- I hope everyone remembers
the context.]

>The result at load time:
>
>> (load "bug2")
>;;; Loading binary file "bug2.sbin"
>;;; Loading binary file "bug1.sbin"
>>>Error: FASLOAD: When compiled, a symbol named DEFMETHOD-TOTO was inherited
>             by the "USER" package; but now, no such symbol is accessible.
...
>... but now I'd rather understand why "defmethod-toto" isn't accessible
>at load time...

The problem is that at compile time (due to the EVAL-WHEN) you are
exporting DEFMETHOD-TOTO, so it's being written to the binary file as an
inherited symbol.  When you later try to load it into a different Lisp
session, you get an error because the symbol hasn't yet been inherited.

There's probably a way to do what you want, but I'm having trouble coming
up with it right now.  Maybe someone else can fill in this gap.

-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Bob Kerns
Subject: Re: Load-time problem with a macro that defines a macro
Date: 
Message-ID: <1991Sep3.222602.1678@crl.dec.com>
In article <······················@Think.COM>, ······@think.com (Barry Margolin) writes:
> In article <····@dione.imag.fr> ······@lifia.imag.fr (Eric Dedieu) writes:
> >Here is a "skinned" piece of code:
> >
> >;;; In a first file "bug1", the global macro, in a general-utility package
> >;;; The created function is put in the same package and then exported,
> >;;; to make things clear.
> >
> >(in-package 'tools)
> >
> >(export '(defmethod-flavor))
> >
> >(defmacro defmethod-flavor (flavor)
> >  `(export
> >     (defmacro ,(intern (string-append "DEFMETHOD-" (symbol-name flavor)) 'tools)
> >         (nom args &body body)
> >       (list 'progn
> >             (cons 'flavors::defmethod
> >                   (cons (list (quote ,flavor) nom)
> >                         (cons args body)))
> >             (list 'identity
> >                   (list 'find-symbol
> >                         (list 'string-append
> >                               ,(symbol-name flavor)
> >                               "-"
> >                               (symbol-name nom)
> >                               "-PRIMARY")))))
> >    'tools))
> 
> First of all, it would help alot if you would use backquote in the inner
> macro as well as the outer one.  I can't parse the above.

Me either.

Anyway, it would certainly help to do the export  before  you
do the DEFMACRO.  I.e.

(defmacro defmethod-flavor (flavor)
  (let ((macro-name (intern (string-append "DEFMETHOD-" (symbol-name flavor))
			    "TOOLS")))
    `(progn (eval-when (eval compile load)
	      (export '(,macro-name) "TOOLS"))
	    (defmacro ,macro-name (nom args &body body)
	       `(progn ....)))))

But even better, is to not pull this kind of
mung-the-package-on-the-fly stunt at all.  This is Bad Practice,
because there are lots of ways for it to screw you.  All you have to
do is to accidentally type DEFMETHOD-TOTO in a package which inherits
from TOOLS, and you've got problems.

Far better to use DEFPACKAGE to lay out your package structure in
advance, and always load the DEFPACKAGE as the first file of your
system.

The package system functions much better as a static declarative
structure than as a dynamic database.  The convenience you gain
from just defining your function in one place is just not worth
it, when it comes to the package system.

Another problem here is that generating names from other names
like this runs into another sort of package problem.  If you
have two different flavors, named by two different symbols,
which have the same print name, but different packages, you
get a conflict.

I.e. DATABASE:INDEX and LIBRARY:INDEX.