Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deliberate Clojure symbol name limitations or inherited?

Tags:

java

jvm

clojure

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 error

(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.)

like image 563
hawkeye Avatar asked Dec 23 '22 00:12

hawkeye


1 Answers

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.)

like image 146
Michał Marczyk Avatar answered Feb 04 '23 07:02

Michał Marczyk