Or, How the Lisp-n Shall Inherit the Earth
Humans like to name things. Like ourselves, Homo sapiens, Latin for “Primate that has taken leave of its senses.”
Then there are engineers. Engineers like to name things too. Like SCSI, pronounced “scuzzy.” Or WYSIWYG, pronounced “wizzy-wig.” Or TTY, pronounced (I couldn’t believe this at first) “titty.”
Then there are programmers, who are singularly uncreative when it comes to naming things. With Apache’s Gregor Samsa class as a brilliant exception, programmers tend to give their variables and functions dull, predictable names like string
, number
, or do_not_use_this_variable_ever
.
Of course, in most programming languages a given name can refer to one thing and one thing only for ever and ever until your program crashes and dumps core Amen. To get around this sad limitation, programming boldly took advantage of the eighth-century’s greatest typographical innovation, lower case letters. Thus we have the Dada-esque beauty of case-sensitive code:
Widget* WIDGET = new Widget.widget();
As languages got more complex, adding first-class functions and first-class types, there were more and more things that needed to be called by the same name. Few programming languages picked up the idea of meaning based on context. So hierarchical namespaces were born:
java.awt.event.ActionEvent myEvent = new java.awt.event.ActionEvent();
My tendinitis starts acting up at the mere sight of that.
Common Lisp to the rescue! As many have pointed out, CL is not just a Lisp-2 (functions and variables in separate namespaces), but more of a Lisp-n. It has many overlapping namespaces — functions, variables, type specifiers, block names, tagbody tags. With a few macros and a hash table, one can easily add a new namespace to the language. For example, testing frameworks like FiveAM and LispUnit put test names in a separate namespace. A programmer using these libraries doesn’t have to think about the extra namespace. Just type symbols and Lisp will do the right thing.
I like to think of this approach as parallel namespaces. Everything is kept neatly separated, without endless qualifiers attached to every symbol. Macros determine how symbols will be evaluated — or not — in a block of code.
This isn’t perfect. For one thing, there’s no canonical way to add a new namespace to a language. Some macros require names to be quoted symbols, some not. Some use keywords. Sometimes an unquoted symbol will pollute the namespace of the current package; sometimes it won’t.
The solution, to my mind, would be something that lets us do this:
(define-namespace foo)
(defmacro-with-namespaces my-macro ((arg1 foo))
. . . )
Then my-macro
would always treat its first argument as a symbol which is not evaluated, internally binding arg1
to whatever value the given symbol holds in the foo
namespace. I’m not going to attempt to implement such a beast just now, but I’m sure an experienced macro-writer (i.e. someone else) could dash it off in half an hour.
I like lisp, although I’m barely more than a rank neophyte with it, but bolstering an argument by misusing the “other” language hardly does anyone any good. Since you’re quoting Java (and with full text justification, gak!), at least use IDIOMATIC java:
;; Assuming an import up here somewhere…, akin to the …with-namespaces)
ActionEvent myEvent = new ActionEvent();
which might still be too wordy for some, it’s a lot less than you make it out to be.
> Everything is kept neatly separated, without endless qualifiers attached to every symbol.
And truly, other languages, including the one you quoted, allow this as well.
Isn’t it already done with packages?
Like :
(in-package “BLA”)
(defmacro blabla (bla) ….. )
I’m not sure the syntax is correct, but to give the idea, you know.
Alex Says:
Isn’t it already done with packages?
Not exactly. Packages are namespaces but they can’t overlap. A better example of what I was thinking of:
(defmacro bar-macro ((arg1 :namespace bar))
…)
(defmacro quux-macro ((arg2 :namespace quux))
…)
Now I can have a symbol BAR with different definitions in the namespaces BAR and QUUX. Calling (BAR-MACRO FOO) will use the value of FOO in BAR, calling (QUUX-MACRO FOO) will use the value of FOO in QUUX. Overlapping namespaces, without conflicts.