|
|
|
|
|
by patrec
2038 days ago
|
|
Again this is not fundamentally tied to whether functions and variables share a namespace, in clojure they do and yet your example above does not cause a problem. Watch: Let's start out with a function: user=> (defn enlist [x] (if (list? x) x (list x)))
#'user/enlist
user=> (let [list 1] (enlist list))
(1)
Create a (pointless) equivalent macro: user=> (defmacro enlist' [x] `(let [y# ~x] (if (list? y#) y# (list y#))))
user=> (let [list 1] (enlist' list))
(1)
No hygiene and yet it works!How? The secret is that backquote is automatically namespace qualifying: user=> '(list 1 2 3)
(list 1 2 3)
user=> `(list 1 2 3)
(clojure.core/list 1 2 3)
In practice this + convenient gensym syntax (also demonstrated above) seems to be enough, just like in practice Common Lisp's approach of separate value and function cells for symbols + prohibition of rebinding function cells of symbols in the standard COMMON-LISP namespace is. Scheme has no cl style namespaces and no prohibition on redefining standard bindings and thus came up with a much more complex macro approach, at the gain of stronger hygiene guarantees (but see article above!) and the cost of an ugly design of considerable complexity that has a completely different sublanguage (non-orthogonally) baked in.As far as purely hygiene is concerned that's IMO not a good trade-off at all; I could rattle off a long list of major usability gripes with both cl and clojure but problems caused by lack of macro hygiene wouldn't be on it. Racket, which is basically several iterations beyond R*RS macros, OTOH is interesting because it's more principled approach gives you something qualitatively different to what you can do with clojure (no read-syntax control; bad error messages) and common lisp (readtables suck and break tooling; bad error messages). |
|