Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Syntax and reader by
retagged by

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!");
    }
}

1 Answer

0 votes
by
...