Search code examples
clojuretype-inference

Clojure shortfall on type inferencing


This code is not my "real" code but is a sample of the issue from a much larger algorithm. It took me a while before I found (with (set! *warn-on-reflection* true)) that reflection was the issue.

With type hinting coming to dynamic languages (e.g. python and clojure) and aggressive type inferencing coming to others (e.g. Scala), it seems odd that I have to explicity cast with (int ...).

Why doesn't clojure know that (aget int-array int) returns an int?

Can I annotate the if statement myself to say it returns an int?

If not, can I use an inline function to avoid the relfection.

(let [a (int-array [4 5 6 7])]
   ;(aset a 1 (int (if (seq a) 40 (aget a 0))))) ;; fast
    (aset a 1      (if (seq a) 40 (aget a 0))))  ;; slow

I know that type hinting is no less typing than this int call but that may not be the case in more complex code.


Solution

  • NOTE: tested manually as of Clojure 1.8.0

    Why doesn't clojure know that (aget int-array int) returns an int?

    It does. What it doesn't seem to know is that (if <<condition>> Long int) returns a result that could be coerced to a primitive int. Note that if you write (int 40) instead, the reflection warning goes away.

    Can I annotate the if statement myself to say it returns an int?

    Using (int ...) is really the way to do that. An int type hint in this case would not be correct, since as we've seen this expression may return a long.

    With type hinting coming to dynamic languages (e.g. python and clojure) and aggressive type inferencing coming to others (e.g. Scala), it seems odd that I have to explicity cast with (int ...)

    Clojure is essentially a dynamically typed language. The Clojure compiler already does quite a bit of type inference -especially locally- but that's still on a best-effort basis, and I don't think anything else should be expected (keep in mind that not knowing the types of the values that flow around the programs is a feature of dynamic typing). Granted, the particular snippet of code that you pasted is amenable to quite a bit of static analysis, but in a typical real-world program where values flow through a lot of intermediaries, such extensive analysis wouldn't even be feasible - therefore, why should the compiler go out of its way to support it?