Search code examples
arraysclojureinteroplong-integer

Why do int arrays bound with 'def' accept Long values in Clojure?


I've noticed that if I def an int array and set an element in the array with a Long, then there are no complaints. However if I bind the int array in a let block set an element with a Long, then an IllegalArgument exception is thrown. Could someone help me understand why this is?

The code below demonstrates the discrepancy. I tried it in both Clojure 1.8 and the latest beta version of 1.9 and got these results.

(def a (int-array 10))
(aset a 0 Long/MAX_VALUE) ;; sets first element to -1

(let [b (int-array 10)]
  (aset b 0 Long/MAX_VALUE)) ;; throws java.lang.IllegalArgumentException: Value out of range for int:

Solution

  • This discrepancy is caused because type inference is occurring in the let, but not in the def. You can verify this by using type hints to switch the situation around:

    (def ^"[I" a (int-array 10))
    (aset a 0 Long/MAX_VALUE)
    ;; throws java.lang.IllegalArgumentException: Value out of range for int:
    
    (let [^Object b (int-array 10)]
      (aset b 0 Long/MAX_VALUE))
    ;; sets first element to -1
    

    Or, alternatively:

    (def a (int-array 10))
    (aset ^"[I" a 0 Long/MAX_VALUE)
    ;; throws java.lang.IllegalArgumentException: Value out of range for int:
    
    (let [b (int-array 10)]
      (aset ^Object b 0 Long/MAX_VALUE))
    ;; sets first element to -1
    

    This is because Clojure inlines calls to aset when possible, which includes all these cases, but the inlined static method call has many overloads.