Created
March 20, 2017 13:06
-
-
Save pjstadig/6fa4ebf465f6d438b4dcc2a5bfbb3db6 to your computer and use it in GitHub Desktop.
A little type hint hint
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns typehint.core) | |
;; A little type hint hint... | |
(set! *warn-on-reflection* true) | |
;; There are two ways to type hint the return value of a function, but one is | |
;; (technically) wrong: | |
(defn ^String as-string0 | |
[obj] | |
(str obj)) | |
(defn as-string1 | |
^String [obj] | |
(str obj)) | |
(.length (as-string0 '(:a :b))) ; <- no reflection warning | |
(.length (as-string1 '(:a :b))) ; <- no reflection warning | |
;; But the first actually means something more than just the return value of the | |
;; function, it means the value of the var is a String. | |
(def ^String a-string | |
"Hello, World!") | |
(defn to-string0 | |
[] | |
(.length a-string) ; <- no reflection warning | |
(.length as-string0) ; <- weird, but no reflection warning | |
) | |
(defn to-string1 | |
[] | |
(.length as-string1) ; <- weird, and reflection warning | |
) | |
;; Why is this? Originally, Clojure did not use type hints on the argument | |
;; vector. To type hint the return value of a function you would put a type | |
;; hint on the var (the first method). In version 1.3 Clojure got primitive | |
;; arguments and return values for functions, and those are indicated by putting | |
;; the type hint on the args and on the arg vector (the second method). | |
;; | |
;; AFAIK, the only reason the first method even works for return values is for | |
;; backwards compatibility. If you put a type hint on the var you are saying | |
;; the value of that var will be that type. The correct way to type hint a | |
;; return value is to put the hint on the arg vector. | |
;; | |
;; This also means you can have different return hints for different arities | |
;; (though I would question why you'd want that!). | |
(defn ambiguous | |
(^String [] | |
"foo") | |
(^Integer [_] | |
(int 5))) | |
(defn confusing | |
[] | |
(.length (ambiguous)) ; <- no reflection warning | |
(.longValue (ambiguous nil)) ; <- also no reflection warning | |
) | |
;; == SUMMARY | |
;; If you want to type hint the *content* of a var, put the type hint on the | |
;; var. | |
;; If you want to type hint the return value of a function, put the type hint on | |
;; the argument vector. |
@cursive-ide I've tried to reproduce that problem with clojure 1.5, 1.6 and 1.7 but I couldn't. But given that it's no longer a problem with 1.8, maybe it's not so relevant anymore.
@borkdude : it's definitely a real problem, often in various projects I had to qualify imports because 1.7 was part of the CI matrix and was failing
@vemv I consider 1.7 too old now to bother adding support in clj-kondo.
@borkdude There is at least one case which is still relevant in modern versions: https://clojure.atlassian.net/browse/CLJ-1180 (fixed in 1.11)
@cursive-ide Thanks.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think @Bronsa pointed this out on Twitter, but in Clojure <= 1.7, when you type hint the arg vector the symbol is not evaluated. This means that if you type hint with the short name of a class you have imported where the function is defined, if that same class is not imported in the namespace where the function is invoked, things will break: