[Openmcl-devel] with-package

Ron Garret ron at flownet.com
Sat Jan 12 08:45:54 PST 2013


You probably ought to read this:

http://www.nhplace.com/kent/PS/Ambitious.html

On Jan 12, 2013, at 8:27 AM, Pascal J. Bourguignon wrote:

> Taoufik Dachraoui <dachraoui.taoufik at gmail.com> writes:
> 
>> On Sat, Jan 12, 2013 at 3:12 PM, Pascal J. Bourguignon <
>> pjb at informatimago.com> wrote:
>> 
>>    Taoufik Dachraoui <dachraoui.taoufik at gmail.com> writes:
>> 
>>> On Sat, Jan 12, 2013 at 2:36 PM, Pascal J. Bourguignon <
>>> pjb at informatimago.com> wrote:
>>> 
>>>     Taoufik Dachraoui <dachraoui.taoufik at gmail.com> writes:
>>> 
>>>     > Hi
>>>     >
>>>     > I am trying to define a macro as follows:
>>>     >
>>>     > (defmacro with-package ((&rest names) &body body)
>>>     >   `(progn
>>>     >      (use , at names)
>>>     >      , at body
>>>     >      (unuse ,@(reverse names))))
>>>     >
>>>     > The issue is that the body may use symbols defined in one of
>>>     > the names (packages) and not in the current package
>>> 
>>>     "Current" WHEN?
>>> 
>>>     > How to do this? I tried with eval-when but I do not know
>>>> how to use
>>>     > it correctly
>>> 
>>>     What are the situations available to eval-WHEN?
>>> 
>>> Current package when you call with-package 
>> 
>>    Ok.  When you call with-package, it's at run-time.  And at
>>    run-time you
>>    can use the current package bound to *package* without any
>>    difficulty.
>> 
>>    With:
>> 
>>        (defun use*   (syms) (format t "I'll use ~S~%" syms))
>>        (defun unuse* (syms) (format t "I unused ~S~%" syms))
>> 
>>        (defmacro use    (&rest syms) `(use* ',syms))
>>        (defmacro unuse  (&rest syms) `(unuse* ',syms))
>> 
>>        (defmacro with-package ((&rest names) &body body)
>>          `(progn
>>             (use , at names)
>>             , at body
>>             (unuse ,@(reverse names))))
>> 
>>        (defpackage :p1 (:export :*v*))
>>        (defparameter p1:*v* 42)
>> 
>> 
>>    we get:
>> 
>>        cl-user> (let ((*package* (find-package :p1)))
>>                   (with-package (a b)
>>                     (let ((s (find-symbol "*V*" *package*)))
>>                       (print (list s (symbol-value s)))
>>                       (terpri))))
>>        I'll use (common-lisp-user::a common-lisp-user::b)
>> 
>>        (*v* 42)
>>        I unused (common-lisp-user::b common-lisp-user::a)
>>        nil
>> […]
>>> ;;; (share '(fn x (+ x 1))) must be evaluated (use calculus), where
>>> share is imported from the package calculus
>> 
>>    It does.  See above, my macro USE is called at run-time.
>> […]
>>    WHEN are they interned?  At READ-TIME!
>> 
>>    Does the operator COMMON-LISP-USER:SHARE use the value of
>>    CL:*PACKAGE* at
>>    RUN-TIME?
>> 
>>    See my example, I used CL:*PACKAGE* at run-time twice:
>> 
>>    - once explicitely by passing *package* to find-symbol.
>> 
>>    - once implicitely by calling print which uses *package* to
>>    determine
>>      how to print symbols.
>> 
>> 
>>    Perhaps you could have a look at:
>>    http://www.nhplace.com/kent/PS/Ambitious.html
>> 
>> Not exactly what I meant
>> 
>> (use file) ;will create a package <current-dir>/file.lisp with a
>> nickname file
>> and load the file, then it imports all the external symbol from file
>> (it first saves the
>> symbols with the same name, if they exist, in the current package
>> *package*)
>> 
>> (unuse file) ;; unintern all external symbol of file and restores the
>> saved symbols (if they exist)
> 
> Ok.  There were a few implicit messages in my posts.
> 
> 1- A: Because it messes up the order in which people normally read text.
>   Q: Why is top-posting such a bad thing?
>   A: Top-posting.
>   Q: What is the most annoying thing on usenet and in e-mail?
>   ----------> http://www.netmeister.org/news/learn2quote.html <-----------
>   ---> http://homepage.ntlworld.com/g.mccaughan/g/remarks/uquote.html <---
> 
> 
> 2- Where is the source of your USE and UNUSE macros? 
> 
>   How do you hope we debug your code without providing self-contained
>   runnable code?
> 
>   But all right, it's not so much a debugging problem than a
>   conceptual error you have, but having runnable code would help us
>   show you how it's wrong.
> 
> 
> 3- the code in the body of your with-package macro cannot be read  TIME
>   you want (after it is read), but it is read TOO EARLY
> 
> 
> 4- Kent's paper explains the conceptual problem you have.  Not what you
>   want, but what you need to understand.
> 
> 
>> I want to find a solution to be able to use with-package such that
>> the imported symbols from calculus are interned in the current
>> package  before reading and/or compiling the body of with-package
> 
> Right.  But no.  At least not using the standard Common Lisp mechanisms
> that are involved in such a form.
> 
> 
> 
> 
> 
> The REPL is specified as performing a Read Eval Print Loop.  That is
> basically:
> 
>    (loop (print (eval (read))))
> 
> (plus some amenities like printing prompts, printing multiple values,
> handing errors, commands, etc).
> 
> So clearly, it first reads a whole sexp.  That means, INTERNing the
> symbols using the value bound to cl:*package* WHEN it is READ.
> 
> Then to evaluate the form, it may have to expand macros WHEN it is
> COMPILED (at least by minimal compilation).
> 
> Then it evaluates the form that has been read and macro-expanded,
> possibly using the values bound to global variables such as cl:*package*
> WHEN it is RUN. 
> 
> Finally it prints the results.
> 
> 
> 
> In the case of LOAD, it is specified to do the same, only while reading
> a file or a file-stream.  Basically, the only difference between the
> REPL and LOAD, is that during LOAD, *load-pathname* and *load-truename*
> are bound, but this is still RUN-TIME (situation :EXECUTE of eval-WHEN).
> Otherwise load can also load .fasl files, in which case it's the
> situation :LOAD-TOPLEVEL.
> 
> 
> And finally, COMPILE-FILE will also read a file, but instead of
> evaluating it, it will compile it.  This eval-WHEN situation is
> :COMPILE-TOPLEVEL.
> 
> Notice here a very important sentence: COMPILE-FILE WILL NOT EXECUTE THE
> CODE THAT IT COMPILES!  (that is, unless you wrap it in a (eval-when
> (:compile-toplevel) …) of course).
> 
> 
> 
> You have several tools and several solutions to your problem.
> 
> 1- there are reader macros.
> 2- there are macros.
> 3- there are functions.
> 4- there is EVAL-WHEN.
> 5- there is evaluation at (source) read-time.  (reader macro, #.)
> 6- there is evaluation at macro-expansion time (macro)
> 7- there is evaluation at compilation-time
>    (eval-when :compile-toplevel).
> 8- there is evaluation at when the binaries (.fasl) are read
>    (eval-when :load-toplevel).
> 9- there is evaluation at when the source (.lisp) are read
>    (eval-when :execute).
> 10- there is evaluation at run-time.
> 
> 
> 
> But given that INTERN (when called by READ) uses the value of the
> variable CL:*PACKAGE* at READ-TIME, and that setting or binding a
> variable can only be done at RUN-TIME, you need to shift a RUN-TIME into
> READ-TIME, or _BEFORE_ READ-TIME of the concerned forms.
> 
> 
> 
> Notice how the REPL or LOAD loops will intercalate READ-TIME,
> COMPILATION-TIME (MACRO-EXPANSION TIME) and RUN-TIME.
> 
>       (loop                     ; "repl"-time
>          (print                 ; "repl"-time
>              (eval              ; run-time
>                                 ;  which includes a:
>                                 ;    compilation-time (at least minimal
>                                 ;                     compilation)
>                                 ;    which includes a:
>                                 ;      macro-expansion time
>                (read))))        ; read-time
> 
> 
> so it goes:
> 
>   read-time
>   macroexpansion-time
>   compilation-time
>   run-time
>   … print
>   … loop
>   read-time
>   macroexpansion-time
>   compilation-time
>   run-time
>   … loop
>   read-time
>   macroexpansion-time
>   compilation-time
>   run-time
> 
> etc.
> 
> 
> So you can first read a form that will set the CL:*PACKAGE* variable
> during its run-time.
> 
> And then read ANOTHER form that will use the CL:*PACKAGE* variable
> during its read-time.
> 
> 
> For example:
> 
>    (in-package "CALCULUS")
> 
>    (share '(f x y))
> 
>    (in-package "COMMON-LISP-USER")
> 
>    (print 'hello)
> 
> 
> 
> Notice what in-package expands to:
> 
>    (macroexpand '(in-package "CALCULUS"))
>    --> (eval-when (:execute :load-toplevel :compile-toplevel)
>          (setf *package* (find-package "CALCULUS")))
>        t
> 
> 
> In particular, it is evaluated at compilation time, so that the next
> forms read by the compiler are read by interning in the new package!
> 
> 
> 
> So you can just write in your files:
> 
>    (use "CALCULUS")
>    (share '(fn x (+ x 1)))
>    (unuse "CALCULUS")
> 
> 
> 
> 
> 
> Using reader macros, you could execute something like (use "CALCULUS")
> at read-time, _BEFORE_ reading the "body".
> 
> For example, you could write a reader macro on #\{ and use it as:
> 
>    { "CALCULUS"
>      (share '(fn x (+ x 1)))
>    }
> 
> This is not a very good idea, because reading this would have those side
> effects of USE and UNUSE.   Editors and other tools may need to read
> forms several times, there may be other threads using the current
> package, etc, so side effects at read-time are even worse than at
> run-time.
> 
> 
> 
>    (defparameter *pstack* '())
> 
>    (defun use* (pname)
>      (push *package* *pstack*)
>      (setf *package* (find-package pname)))
> 
>    (defun unuse* (pname)
>      (declare (ignore pname))
>      (setf *package* (pop *pstack*)))
> 
> 
> 
>    (set-macro-character #\{ 'read-with-package nil)
> 
>    (set-syntax-from-char #\} #\))
> 
>    (defun read-with-package (stream ch)
>      (declare (ignore ch))
>      ;; This is READ-TIME !
>      (let ((pname (read stream)))  
>        (use* pname) ; Side effect at read-time, BAD!
>        (unwind-protect ; against further read errors.
>             (let ((form (read-delimited-list #\} stream)))
>               (if (= 1 (length form))
>                   form
>                   `(progn , at form)))
>          (unuse* pname))))
> 
>    (defpackage :calculus (:use :cl))
> 
>    cl-user> '{"CALCULUS" (share '(fn x (+ x 1)))}
>    (calculus::share '(calculus::fn calculus::x (+ calculus::x 1)))
> 
>    cl-user> '{"CALCULUS" (share '(fn x (+ x 1))) (print 'share)}
>    (progn
>       (calculus::share '(calculus::fn calculus::x (+ calculus::x 1)))
>       (print 'calculus::share))
> 
> 
> But notice that this is only a read-time operation.  When the forms read
> are evaluated, we are back to the run-time binding of *package*.  Which
> is the opposite of what you asked first.  (But that's all right, you
> didn't know what you were asking).
> 
> 
> 
> 
> 
> Now, you could also define a reader macro for #\( so that you could
> maskerade a form like (with-package ("CALCULUS") …) as a normal lisp
> operator form, while being a read-time reader macro.  It's possible but
> it would be very very bad, because it would mislead any seasonned
> lisper.  (On the other hand, seasonned lispers should be ready to be
> misled this way at any time >:-}).
> 
> 
> 
> 
> There are also some implementations that expand the symbol qualification
> syntax to let you read things as:
> 
>     'calculus::(share '(fn x (+ x 1)))
>     --> (calculus::share '(calculus::fn calculus::x (+ calculus::x 1)))
> 
> You could implement reader macros to do that portably, you would only
> have to define a reader macro on all the characters that may start a
> package name.  (Or use a future lisp reader hook when it'll be specified
> and implemented ;-)).
> 
> 
> -- 
> __Pascal Bourguignon__                     http://www.informatimago.com/
> A bad day in () is better than a good day in {}.
> 
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel




More information about the Openmcl-devel mailing list