In Common LISP I can do:
(setf a1 'a)
(setf 1a 'b)
In clojure I can do the first (ignoring the fact that setf and def work differently)
(def a1 'a)
but with the second I get an er开发者_运维知识库ror
(def 1a 'b)
java.lang.NumberFormatException: Invalid number: 1a
Did Clojure just inherit this limitation from Java, or is it deliberate? (ie you can't have a class name, variable or method name with Java with this style - so I assume it has just carried across.)
Clojure's symbol literals are documented to need to start with a non-numeric character. This has nothing to do with Java identifier or numeric literal syntax -- a Clojure symbol literal is whatever clojure.lang.LispReader
's read
method reads in as a symbol and there's a number of characters permitted within Clojure symbol literals which are not admissible in Java identifiers (e.g. -
, >
...; there's also a scheme for translating those into character sequences such as _GT_
for >
for interop purposes). The direct cause for the error is that clojure.lang.LispReader/read
dispatches to readNumber
immediately upon seeing a digit and there's no way to "back out" of that.
A tangential discussion for the sake of completeness.
Note that if you construct a symbol by hand, you can use it to name a Var:
;; Clojure's intern serves a different purpose to CL's intern, see (doc intern)
user> (intern *ns* (symbol "1+") inc)
#'user/1+
user> ((ns-resolve *ns* (symbol "1+")) 1)
2
You can even do funky stuff like
user> (eval `(defrecord ~(symbol "1foo") []))
user.1foo
user> user.1foo
user.1foo
user> (user.1foo.)
#:user.1foo{}
...which is of course totally crazy, though perhaps not as much as
user> (in-ns (symbol "1foo"))
#<Namespace 1foo>
1foo> (clojure.core/refer-clojure)
nil
1foo> (defrecord Foo [])
1foo.Foo
1foo> (in-ns 'user)
#<Namespace user>
user> (1foo.Foo.)
; Evaluation aborted. ;; can't do that
user> (eval `(new ~(symbol "1foo.Foo")))
#:1foo.Foo{}
I suppose that if one insisted on doing this sort of things, one would ultimately bump into JVM limitations. There is of course no good purpose to doing so... Anyway, back to the original question, the error caused by 1+
has to do with symbol literal syntax, which is Java-friendly only to the degree that a reasonable "translation" exists. Clojure objects which have names don't care much about those names being well-formed or otherwise, although using funky names is unwieldy and definitely not supported.
(And user.1foo
from the example above is actually a Java class -- I'm a bit surprised to see that this one has actually worked, though on the other hand I seem to recall the JVM's internal limitations on names are supposed to be less strict than those of Java.)
精彩评论