Lisp HUG Maillist Archive

Question for Lisp experts...

There are many times when one wants to locally "use" symbols belonging to another package. We don't want those symbols to be globally visible (directly) in the main body of the code, but inside of some routines, it would be convenient for them to be treated as imported symbols.

Example: within a large body of code, I have just one routine that calls on a number of functions from another package. Here is one example from the Rice statistics package, where a closed-form solution for the cumulative distribution is intractable, and we must resort to numerical integration of the PDF.

To do that quickly, I utilize lazy streams with iterated acceleration of partial sums. Acceleration is by Euler's extrapolation.

(defun integrate (f a b)
  (lzs:within (lzs:relerror 1d-8)
              (lzs:accelerate-series #'lzs:euler
                                     (lzs:integrate f a b))))


Here you see that all those routines being used live in the LZS package. This is the only place that LZS symbols are being used, but I want to avoid potential collisions with LZS symbols in the remainder of the code in this present package.

OCaml has a construct "with" that permits the introduction of symbols that can assume a "use" from the module named in the "with" clause. That allows local importation of the symbols for the body of the "with" clause. 

Is there anything similar in Lisp?

Dr. David McClain
Chief Technical Officer
Refined Audiometrics Laboratory
4391 N. Camino Ferreo
Tucson, AZ  85750

email: dbm@refined-audiometrics.com
phone: 1.520.390.3995
web: http://refined-audiometrics.com



Re: Question for Lisp experts...

On Wed, 9 Jul 2008 10:26:21 -0700, David McClain <dbm@refined-audiometrics.com> wrote:

> OCaml has a construct "with" that permits the introduction of
> symbols that can assume a "use" from the module named in the "with"
> clause.  That allows local importation of the symbols for the body
> of the "with" clause.
>
> Is there anything similar in Lisp?

Not in the ANSI standard, but as this is Common Lisp and you can
control the reader you can probably hack something like this
yourself... :)

Or look here:

  http://www.tfeb.org/lisp/hax.html#READ-PACKAGES

Cheers,
Edi.


Re: Question for Lisp experts...

David McClain <dbm@refined-audiometrics.com> writes:

>
> OCaml has a construct "with" that permits the introduction of symbols
> that can assume a "use" from the module named in the "with" clause.
> That allows local importation of the symbols for the body of the
> "with" clause.
>
> Is there anything similar in Lisp?

I don't know the exact semantics of Ocaml's with, but perhaps the
following WITH-RELOCATED-SYMBOLS will match it closely enough:

  CL-USER> (defmacro with-relocated-symbols ...) ; see below

  CL-USER> (defpackage :foo (:use))    ; no :CL!

  CL-USER> (in-package :foo)

  FOO> '(let ((x 42)) (funcall 'sqrt x))
  (LET ((X 42)) (FUNCALL 'SQRT X))

  FOO> (let ((x 42)) (sqrt x))
  ...error...

  FOO> (cl-user::with-relocated-symbols (:package :cl)
         '(let ((x 42)) (funcall 'sqrt x)))
  (CL:LET ((X 42))
    (CL:FUNCALL 'CL:SQRT X))

  FOO> (cl-user::with-relocated-symbols (:package :cl)
         (let ((x 42)) (funcall 'sqrt x)))
  6.4807405
  

HTH,

  -T.
  


(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun map-improper-list (fn list)
    "Like CL:MAPCAR but works on any kind of list.

Returns a fresh list that is the same kind of list as LIST \(i.e.
circular if LIST is circular, dotted if LIST is dotted, proper
if LIST is proper.\)

Each element in the new list is the result of invoking FN on the
respective element in LIST; if LIST is dotted, FN is also called on
the dotted element."
    ;; Hrm, this code has the sex appeal of Lisp code from the 70s..
    (if (null list)
	list
	(flet ((yield  (x) (funcall fn x))
	       (finish (r) (return-from map-improper-list r)))
	  (do* ((result (list (yield (car list))))
		(ptr    result)	                ; pointer to last cell in RESULT
		(slow (cdr list)  (cdr slow))	; tortoise
		(fast (cddr list) (cddr fast))) ; hare
	       (nil)
	    (cond ((null slow)   (finish (prog1 result (setf (cdr ptr) nil))))
		  ((atom slow)   (finish (prog1 result (setf (cdr ptr) (yield slow)))))
		  ((eq slow fast)               ; circle?
		   ;; grab remaining elements:
		   (loop for el in slow until (eq el (first list)) do
			 (setf (cdr ptr) (list (yield el)))
			 (setf ptr (cdr ptr))
			 finally
		           (setf (cdr ptr) result) ; make RESULT circular.
		           (finish result)))
		  (t
		   (setf (cdr ptr) (list (yield (car slow))))
		   (setf ptr (cdr ptr))
		   (when (atom fast)    ; we're in a dotted list...
		     (setf fast nil))))	; FAST is of no interest anymore.
	    )))))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun map-atoms (fn sexp)
    (if (atom sexp)
	(funcall fn sexp)
	(map-improper-list #'(lambda (sub-sexp) (map-atoms fn sub-sexp))
			   sexp))))

(defmacro with-relocated-symbols ((&key (package *package*)) &body body)
  "Remaps all symbols in BODY whose symbol-names can be found in
PACKAGE, into PACKAGE."
  (flet ((maybe-relocate (atom)
	   (if (not (symbolp atom))
	       atom
	       (or (find-symbol (symbol-name atom) package) atom))))
    ;; This won't work properly if BODY contains (evil) macros that
    ;; will create new symbols under the hood. For that, we'd have to
    ;; MACROEXPAND-ALL the body first which unfortunately not in ANSI.
    `(progn ,@(map-atoms #'maybe-relocate body))))


Updated at: 2020-12-10 08:42 UTC