views:

328

answers:

2

Still struggling to understand what best practices are with respect to macros. I'm attempting to write a macro which defines packages on the fly.

(defmacro def-dynamic-package (name)
  `(defpackage ,(intern (string-upcase name) "KEYWORD")
     (:use :common-lisp)))

This works fine only for expressions such as:

(def-dynamic-package "helloworld")

But fails miserably for something like this:

(defun make-package-from-path (path)
  (def-dynamic-package (pathname-name path)))

or

(defun make-package-from-path (path)
  (let ((filename (pathname-path)))
     (def-dynamic-package filename)))

I understand how most basic macros work but how to implement this one escapes me.

A: 

Failure is to be expected here, because a macro is used when its argument should not be evaluated.

In your first make-package-from-path, the def-dynamic-package will receive as argument a list that is EQUAL to the value of the following expression:

(list 'pathname-name 'path)

In your case, you only want a function:

(defun def-dynamic-package (name)
  (defpackage (string-upcase name)
    (:use :common-lisp)))

BTW, if you check the CLHS, you'll see that the first argument of defpackage needn't be a symbol, but any string designator.

Nowhere man
Thanks much, I've had the feeling that learning Common Lisp is a lot about learning when to and when not to use macros.
dnolen
What you're doing there is defining a package called “(STRING-UPCASE NAME)” -- at least that would be what you were doing if a list were a valid string designator. In any case, your code doesn't portably work.
Matthias Benkard
A couple of issues: a) def* usually names macros and b) see Matthias' answer where he mentions MAKE-PACKAGE. (Your function doesn't work because DEFPACKAGE doesn't evaluate its first argument.
Luís Oliveira
Ha, crap. I didn't copy-pasted my working code... I always tell people that's the reason you always run actual code and then copy-paste it.
Nowhere man
+5  A: 

defpackage is a macro. As such, it's expanded at compile-time, not run-time. What you want is something that is called at run-time in order to make a new package. Therefore, defpackage can't do anything for you.

Fortunately, there's also make-package, which provides defpackage's features as a function. Use it instead of defpackage.

Matthias Benkard
Ah, this was one of the issues I had designing a macro, that defpackage itself was a macro was making it difficult for me to reason about a good approach. Thanks.
dnolen
Actually, it's defpackage that provides make-package's features as a handy macro. :-)
Luís Oliveira