Boolean return type-hint confusing the compiler
Description
Environment
OSX, Clojure 1.6.0
Activity
Alex MillerJanuary 19, 2022 at 9:21 PM
Should error per https://clojure.atlassian.net/browse/CLJ-790#icft=CLJ-790
Andy FingerhutMarch 12, 2015 at 2:36 PM
Mikkel: If the type tags are Java classes, not primitives, then ^Classname is a correct type tag. If you use Eastwood, it can warn about these incorrect type tags, and has some documentation on what works and what does not here: https://github.com/jonase/eastwood#wrong-tag
Also here: https://github.com/jonase/eastwood#unused-meta-on-macro
importMarch 12, 2015 at 12:36 PM
Comment made by: kamstrup
Thanks for clarifying Nicola, you are indeed correct.
Putting return type annotations before the method name seems to be common practice in a lot of Clojure code I've read online. Perpetuated by some online tutorials, and the clojure.org docs them selves (fx.
(defn ^:private ^String my-fn ...)
is found in http://clojure.org/cheatsheet)
Nicola MomettoMarch 12, 2015 at 12:01 PM
Metadata on def's symbol is evaluated as per the doc (http://clojure.org/special_forms), evaluating `boolean` results in the clojure.core/boolean function which is not a valid type hint.
As a rule of thumb, attach the return tag in the argvec rather than on the def symbol, in this case you should write
(defn foo-bar?
^boolean [node]
(= node "foo-bar"))
I understand why the fact that
(defn ^boolean foo [] true)
and
(defn foo ^boolean [] true)
behave differently and the fact that the compiler will throw iff the type hint is used rather than throwing at the function definition time is confusing (and I've complained about this and the lack of documentation/specification regarding type hints for a while) but this is not a bug
importMarch 12, 2015 at 11:25 AM
Comment made by: kamstrup
Typo in comment 2 in the snippet: s/xtc-scenario?/foo-bar?/
Saving the below snippet and running it like
Produces
$ java -jar clojure-1.6.0.jar snippet.clj Exception in thread "main" java.lang.IllegalArgumentException: Unable to resolve classname: clojure.core$boolean@1356d4d4, compiling:(/Users/kamstrup/tmp/snippet.clj:15:1) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6651) at clojure.lang.Compiler.analyze(Compiler.java:6445) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6632) at clojure.lang.Compiler.analyze(Compiler.java:6445) at clojure.lang.Compiler.access$100(Compiler.java:38) at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:538) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6644) at clojure.lang.Compiler.analyze(Compiler.java:6445) at clojure.lang.Compiler.analyze(Compiler.java:6406) at clojure.lang.Compiler.eval(Compiler.java:6707) at clojure.lang.Compiler.load(Compiler.java:7130) at clojure.lang.Compiler.loadFile(Compiler.java:7086) at clojure.main$load_script.invoke(main.clj:274) at clojure.main$script_opt.invoke(main.clj:336) at clojure.main$main.doInvoke(main.clj:420) at clojure.lang.RestFn.invoke(RestFn.java:408) at clojure.lang.Var.invoke(Var.java:379) at clojure.lang.AFn.applyToHelper(AFn.java:154) at clojure.lang.Var.applyTo(Var.java:700) at clojure.main.main(main.java:37) Caused by: java.lang.IllegalArgumentException: Unable to resolve classname: clojure.core$boolean@1356d4d4 at clojure.lang.Compiler$HostExpr.tagToClass(Compiler.java:1069) at clojure.lang.Compiler$InvokeExpr.getJavaClass(Compiler.java:3659) at clojure.lang.Compiler$LocalBinding.hasJavaClass(Compiler.java:5657) at clojure.lang.Compiler$LocalBindingExpr.hasJavaClass(Compiler.java:5751) at clojure.lang.Compiler.maybePrimitiveType(Compiler.java:1283) at clojure.lang.Compiler$IfExpr.doEmit(Compiler.java:2631) at clojure.lang.Compiler$IfExpr.emit(Compiler.java:2613) at clojure.lang.Compiler$BodyExpr.emit(Compiler.java:5826) at clojure.lang.Compiler$LetExpr.doEmit(Compiler.java:6180) at clojure.lang.Compiler$LetExpr.emit(Compiler.java:6133) at clojure.lang.Compiler$BodyExpr.emit(Compiler.java:5826) at clojure.lang.Compiler$FnMethod.doEmit(Compiler.java:5374) at clojure.lang.Compiler$FnMethod.emit(Compiler.java:5232) at clojure.lang.Compiler$FnExpr.emitMethods(Compiler.java:3771) at clojure.lang.Compiler$ObjExpr.compile(Compiler.java:4410) at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3904) at clojure.lang.Compiler.analyzeSeq(Compiler.java:6642) ... 19 more
The snippet:
snippet.clj
;; Bug in the Clojure compiler (1.6.0): If we annotate the return here with ^boolean we get: ;; 'IllegalArgumentException: Unable to resolve classname: clojure.core$boolean' from the compiler. ;; Removing it, everything is as expected (defn ^boolean foo-bar? [node] (= node "foo-bar")) ;; Check it out, we can have ^boolean here, but not on foo-bar? !! :-) (defn ^boolean bar-foo? [node] (= node "bar-foo")) ;; Instead of removing the ^boolean return on foo-bar? we can also remove this function ;; to have all work as expected (defn ^boolean interesting? [node] (or (foo-bar? node) (bar-foo? node))) (println "Foo-Bar?" (foo-bar? "baz"))