I tried to hang a WindowListener on a dialog and got this error:
$ clj windowlistener.clj
WARNING: When invoking clojure.main, use -M
Syntax error (IllegalArgumentException) compiling reify* at (windowlistener.clj:9:10).
Can't define method not in interfaces: windowIconified
Here is the code. Do you see the problem?
(import 'javax.swing.JOptionPane
'java.awt.event.WindowListener)
(let [jdialog (.createDialog
(JOptionPane. (System/getProperty "java.version"))
"WindowListener")
wl (reify WindowListener
(windowActivated [this e])
(windowClosed [this e])
(windowClosing [this e])
(windowDeactivated [this e])
(windowDeiconified [this e])
(windowIconified [this e])
(windowOpened [this e]))]
(.addWindowListener jdialog wl)
(.show jdialog))
I know the method name is spelled properly because I pasted it from Firefox's display of the Javadoc [1] and then rearranged parentheses. Ha ha, copying and pasting was my error! Here is a hex dump of what I got:
$ od -tcx1 ~/windowlistener.clj
...
0000700 ( w i n d o w I c
20 20 20 20 20 20 20 28 77 69 6e 64 6f 77 49 63
0000720 o n i f i e d 342 200 213 [ t h i s
6f 6e 69 66 69 65 64 e2 80 8b 20 5b 74 68 69 73
0000740 e ] ) \t \n
What is that 342 200 213 a.k.a. e2 80 8b glop? I guess the file is in UTF-8, in which case those bytes would indicate code point 0x200b (by bit fiddling[2]), which represents ZERO WIDTH SPACE according to unicode.org[3].
(By the way, I have not found those bytes on Oracle's web page. Perhaps Firefox inserted the ZERO WIDTH SPACE between elements for the clipboard's benefit.)
I am amused that the compiler took ZERO WIDTH SPACE as a symbol character. Such behavior is not the same as javac's. According to Character, ZERO WIDTH SPACE is identifier-ignorable:
$ clj
Clojure 1.10.1
user=> (Character/isIdentifierIgnorable 0x200b)
true
Unicode delicately suggests to "narrow" the set of identifier characters "for security" [4], presumably because crafting code with invisible distinctions is a power with no legitimate uses.
Indeed, javac ignores ZERO WIDTH SPACE: stick a ZERO WIDTH SPACE in the middle of the "main" in "public static void main" and your program still works fine.[5] If such a precedent had not been set, I might prefer that the "ignorable" be rejected outright, but I could use a linter for that.
And I am aggrieved that the error message did not make the problem clear. javac sidesteps that challenge by weeding out identifier-ignorable characters in the first place, so there is no invisible aspect that needs exposing. Anyway, not all inadvertent uses of ZERO WIDTH SPACE immediately cause a compiler error. The code I pasted from the javadoc was littered with them, including affixed to the parameter name, where they will not cause an error until later -- and most likely, the error will involve a "correct" spelling, not the infested one.
In sum, may we suggest, for humanitarian reasons, that Clojure keep "identifier-ignorable" characters out of symbols, either by ignoring them (as javac does) or by treating them as an error?
[1] https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/event/WindowListener.html
[2] https://en.wikipedia.org/wiki/UTF-8#Encoding
[3] https://util.unicode.org/UnicodeJsps/character.jsp?a=200B
[4] http://unicode.org/reports/tr31/#Figure_Code_Point_Categories_for_Identifier_Parsing
[5] Which you can do with a Unicode literal:
class A {
public static void m\u200bain(String[] args) throws Exception {
System.out.println("Hello world!");
}
}