Keyword equality check

436 views
Skip to first unread message

Arkadiusz Komarzewski

unread,
Feb 12, 2014, 11:23:00 AM2/12/14
to clo...@googlegroups.com
Hi,

I wonder how is equality of keywords implemented in Clojure?

I have this piece of Java code executed in one classloader:
Keyword a = (Keyword) RT.var("clojure.core", "keyword").invoke("keyword");

Then, when I pass it to another part of my application (which uses another classloader) and do this:
Keyword b = (Keyword) RT.var("clojure.core", "keyword").invoke("keyword");
assert a == b;

Assertion passes.
Why does it work? If I'm correct, keywords are interned, but since they were created in separate classloaders shouldn't that assert fail?

Jozef Wagner

unread,
Feb 12, 2014, 11:45:38 AM2/12/14
to clo...@googlegroups.com
Interning table uses keyword's symbol as a key, and the symbols are compared by value. See https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Keyword.java#L37


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Herwig Hochleitner

unread,
Feb 12, 2014, 12:38:37 PM2/12/14
to clo...@googlegroups.com
I'm willing to bet that both classloaders have the same clojure runtime in a common base classloader.
i.e. that cl1.loadClass("clojure.lang.RT") == cl2.loadClass("clojure.lang.RT");

If the two clojure runtimes were distinct, the assert would indeed fail.
Also .equals return false and this assignment would fail: Keyword kw = getKwFromDifferentClassloader();
This would work: Object kw = getKwFromDifferentClassloader();

See here: http://stackoverflow.com/questions/5375349/class-instance-obtained-through-multiple-class-loader

For reference: This is a printout of the return value of nrepl/start-server in a completely separated classloader, save for bootstrap classes in the java library (an architecture that I use for my services)

{#<Keyword :server-socket> #<ServerSocket ServerSocket[addr=0.0.0.0/0.0.0.0,localport=4003]>,
 #<Keyword :port> 4003,
 #<Keyword :open-transports> #<Atom clojure.lang.Atom@77b8bd8>,
 #<Keyword :transport> #<transport$bencode clojure.tools.nrepl.transport$bencode@56be0fb6>,
 #<Keyword :greeting> nil,
 #<Keyword :handler> #<middleware$wrap_conj_descriptor$fn__693 clojure.tools.nrepl.middleware$wrap_conj_descriptor$fn__693@4608f600>,
 #<Keyword :ss> #<ServerSocket ServerSocket[addr=0.0.0.0/0.0.0.0,localport=4003]>}


Notice how the printer prints out the {} correctly, because it dispatches on java.util.Map (a common base class from the bootstrap classloader), but fails to recognize the Keywords of a different clojure runtime.
From experiments, I can assure you that (not= :port #<Keyword :port>)
Similarly, assoc on this map would fail, because it's only recognized as a j.u.Map, but not as a c.l.PersistentMap.

cheers

Alex Miller

unread,
Feb 12, 2014, 6:32:07 PM2/12/14
to clo...@googlegroups.com
I think it's a little more subtle than that.  Symbols are composed of a String name and a String namespace. When symbols are created they intern each of those Strings. Interned Strings are comparable by identity across the JVM. Symbol equals() compares name and namespace. Keyword extends from Symbol and calls into the Symbol interning and inherits the equals() implementation. 

As Jozef mentions, the Keyword intern table is held in a static map in Keyword and that would not be shared if you had multiple Clojure classloaders. However, I think the Keyword intern table is largely to avoid creating multiple instances of the same Keyword (as well as safely cleaning them up if no one is using them anymore). This is all irrelevant for the equality comparison in the original post.

Alex Miller

unread,
Feb 12, 2014, 6:41:12 PM2/12/14
to clo...@googlegroups.com
Reading a little more closely, that's an identity comparison in Java, not an equals comparison in Clojure (who uses Java anyways? :). So I would retract my last statement. The question is really whether the two classloaders are deferring the load of the common class to a parent classloader that loads the same Keyword class. You can ask the Keyword classes you have for their classloader and then look through the parents to investigate that question.

Arkadiusz Komarzewski

unread,
Feb 13, 2014, 6:04:06 AM2/13/14
to clo...@googlegroups.com
Guys, thank you all for input.

I found that both keywords were (as you expected) loaded by same classloader (same parent was used in both). After fixing that the assert indeed fails.

Cheers!
Reply all
Reply to author
Forward
0 new messages