[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