[Openmcl-devel] with-package

Taoufik Dachraoui dachraoui.taoufik at gmail.com
Sat Jan 12 14:48:58 PST 2013

I managed to write a correct version of with-package macro, this is due to
the valuable hints and discussions, I learned a lot by being a member of
ccl thank you a lot for all your helps

here is the whole code of the mb simple file system (some bugs may be found
sorry i did not test thoroughly)

Now I would like to see why this design is wrong by showing me examples
where the system can fail

Thank you again


(defpackage "MB" (:export "USE" "UNUSE" "WITH-PACKAGE"))
(in-package "MB")

(defun %new-symbol (s package &optional v)
  (setf (symbol-value (intern s package)) v))

(defun %symbol-value (s &optional package)
  (if (null package)
      (symbol-value (find-symbol s))
      (symbol-value (find-symbol s package))))

(defun (setf %symbol-value) (v s &optional package)
  (if (null package)
      (setf (symbol-value (find-symbol s)) v)
      (setf (symbol-value (find-symbol s package)) v)))

(defun %import (s)
  (let ((ss (find-symbol (symbol-name s))))
    (if (not (null ss))
   (if (eq (symbol-package ss) *package*)
       (copy-symbol ss t)
       (cons (symbol-name ss) (symbol-package ss)))
   (gethash (symbol-name s) (%symbol-value "**MB-HASH-HIDDEN-SYMBOLS**")))
  (unintern ss)))
    (import s)))

(defun %unimport (s)
  (if (not (eq (symbol-package s)
       (symbol-package (find-symbol (symbol-name s)))))
      (remhash (symbol-name s)
       (gethash (symbol-name s)
(%symbol-value "**MB-HASH-HIDDEN-SYMBOLS**")))
(unintern (find-symbol (symbol-name s)))
(let ((asym (pop (gethash (symbol-name s)
  (%symbol-value "**MB-HASH-HIDDEN-SYMBOLS**")))))
  (if (not (null asym))
      (if (consp asym)
  (%import (find-symbol (car asym) (cdr asym)))
  (let ((sym (intern (symbol-name s))))
    (setf (symbol-plist sym) (symbol-plist asym))
    (if (boundp asym) (setf (symbol-value sym) (symbol-value asym)))
    (if (fboundp asym)
(if (null (macro-function asym))
    (setf (symbol-function sym) (symbol-function asym))
    (setf (macro-function sym) (macro-function asym)))))))))))

(defun %make-package (file nickname)
  (if (null (file-write-date file))
      (error (format nil "file not found (~S)" file))
      (let ((*package*
     (if (null (find-package nickname))
 (make-package file
       :use '("CCL" "COMMON-LISP" "MB")
       :nicknames `(,nickname))
 (find-package nickname))))
(%new-symbol "**MB-PACKAGE-TIMESTAMP**" *package* (file-write-date file))
(%new-symbol "**MB-HASH-HIDDEN-SYMBOLS**" *package* (make-hash-table))
(load file))))

(defun %use (name)
  (if (and (not (eq (find-package name) *package*))
   (or (null (%symbol-value "**MB-USED-PACKAGES**"))
       (not (eq (find-package name) (car (%symbol-value
(if (null (find-package name))
    (%make-package (format nil "~A/~A.LISP" (ccl::current-directory-name)
(string name)) name)
    (if (and (not (null (find-symbol "**MB-PACKAGE-TIMESTAMP**"
(find-package name))))
     (> (file-write-date (package-name (find-package name)))
(%symbol-value "**MB-PACKAGE-TIMESTAMP**" (find-package name))))
(%make-package (package-name (find-package name)) name)))
(if (null (find-symbol "**MB-HASH-HIDDEN-SYMBOLS**" (symbol-package name)))
    (%new-symbol "**MB-HASH-HIDDEN-SYMBOLS**" *package* (make-hash-table)))
(do-external-symbols (s (find-package name)) (%import s))
(push (find-package name) (%symbol-value "**MB-USED-PACKAGES**")))))

(defun %unuse (name)
"only unuse the last used package"
  (if (not (null (find-package name)))
      (if (eq (find-package name) (car (%symbol-value
  (let ((pkg (pop (%symbol-value "**MB-USED-PACKAGES**"))))
    (do-external-symbols (s pkg) (%unimport s))
    (if (not (null (find-symbol "**MB-PACKAGE-TIMESTAMP**" pkg)))
(delete-package pkg)))
  (error "cannot unuse package ~S~%" (find-package name)))))

(defmacro use (&rest names)
     (if (null (find-symbol "**MB-USED-PACKAGES**")) (%new-symbol
"**MB-USED-PACKAGES**" *package*))
     (dolist (name ',names) (%use name))))

(defmacro unuse (&rest names)
  (if (null names)
      `(%unuse (car (%symbol-value "**MB-USED-PACKAGES**")))
      `(dolist (name ',names) (%unuse name))))

(defmacro with-package ((&rest names) &body body)
  (let ((r (gensym)) (e (gensym)))
       (use , at names)
       (multiple-value-bind (,r ,e)
              (eval (read-from-string
     (format nil "(progn ~{~A ~})"
     (let ((*readtable* (copy-readtable)))
       (set-macro-character #\(
    (lambda (stream char)
      (declare (ignore char))
      (let ((str "") (n 0))
(do ((c (read-char stream) (read-char stream nil 'the-end)))
    ((and (= n 0) (eq c #\))))
  (if (eq c #\() (incf n) (if (eq c #\)) (decf n)))
  (setf str (format nil "~A~A" str c)))
         (unuse ,@(reverse names))
         (if (null ,e)
             (values-list ,r)
             (error (format nil "~S" ,e)))))))

On Sat, Jan 12, 2013 at 8:58 PM, Robert Goldman <rpgoldman at sift.info> wrote:

> On 1/12/13 Jan 12 -9:30 AM, Taoufik Dachraoui wrote:
> > I am wrote a very simple package system that I am using for all my
> > developments, I wanted to add
> > the macro with-package so that I can use a package temporarily and then
> > revert to previous context
> > just before using the package.
> I won't presume to dictate your personal programming style.  However, I
> feel free to *suggest* that this may not work out well.
> The package is effectively a big, invisible global variable, and having
> the correctness of your code depend on it can make its behavior
> unpredictable and difficult to understand.
> This is related to the discussion of DEFPACKAGE in the spec:
> "It is recommended that the entire package definition is put in a single
> place, and that all the package definitions of a program are in a single
> file. This file can be loaded before loading or compiling anything else
> that depends on those packages. Such a file can be read in the
> COMMON-LISP-USER package, avoiding any initial state issues."
> Reading between the lines, I suggest that this recommendation is
> intended to provide a transparent, easy-to-understand interpretation of
> the packages, so that the set of accessible names, and their
> interpretations is easy for the reader of code to predict.  Pushing and
> popping available symbols tends to work against this.  It also, as has
> been pointed out, can involve the need to understand issues of
> compile-time, load-time, and execute-time evaluation.
> I offer an additional heuristic: if something you are doing requires
> attention to the subtleties of evaluation time, consider a different
> method.  This is only a heuristic, because there are important tasks
> that can be done well by exploiting, e.g., compile-time execution
> subtleties.  Nevertheless, it's useful to consider alternatives when
> coding.
> The principle is the same as when writing expository prose: avoid
> complex constructions when they are unnecessary, since making the reader
> spend a lot of effort to understand incurs a risk of misunderstanding.
> Again, it's your code; do what you like with it, but simpler
> constructions may serve you better in the long run.
> Cheers,
> r
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.clozure.com/pipermail/openmcl-devel/attachments/20130112/4fcac11c/attachment.htm>

More information about the Openmcl-devel mailing list