Reputation: 3538
I have a small function used for debugging:
(set! *warn-on-reflection* true)
(defn debug [x] (doto x (->> println :>)))
When I call my function in a loop, I get the following reflection warning:
(loop [i 5] (when (pos? i) (recur (debug (dec i)))))
form-init14269737395101875093.clj:1 recur arg for primitive local: i is not matching primitive, had: Object, needed: long
Auto-boxing loop arg: i
I want to solve the reflection warning. How can I make my function "inherit" the type information from the parameter without explicitly specifying it or replacing it with a macro?
Upvotes: 0
Views: 171
Reputation: 4901
If, for whatever the reason, you do not want to use a macro, you may want to have a look at definline which seems to preserve type information:
(definline debug2 [x] (doto x (->> println :>)))
The call below, for instance, does not result in a reflection warning:
(.add (debug2 (ArrayList.)) 5)
My initial thought would have been to use a Java class with overloaded methods to achieve something along these lines, of which one method would take a generic argument T
and return a T
. In addition to this generic method, you would have had to overload that method for the few primitive types because generics only work with boxed values AFAIK. You could then reuse the class below for your debugging purposes:
import clojure.lang.IFn;
public class PassThrough {
private IFn _fn;
public PassThrough(IFn fn) {
_fn = fn;
}
public <T> T invoke(T x) {
_fn.invoke(x);
return x;
}
public long invoke(long x) {
_fn.invoke(x);
return x;
}
public double invoke(double x) {
_fn.invoke(x);
return x;
}
}
This will not work for reference types, though, because of type erasure. So if I would do something like this, I would still get a warning:
(defn debug [x] (doto x (->> println :>)))
(def pt-debug (PassThrough. debug))
(.add (.invoke ^PassThrough pt-debug (ArrayList.)) 9) ;; <-- Reflection warning here when calling .add
Upvotes: 0
Reputation: 17849
the problem is that the return type of debug
can't be deduced.
this is usually solved with type hints
in your case the following should do the trick:
(defn debug ^long [x] (doto x (->> println :>)))
user> (loop [i 5] (when (pos? i) (recur (debug (dec i)))))
4
3
2
1
0
nil
Upvotes: 1
Reputation: 29984
Here is a way that works:
(loop [i (Integer. 5)]
(when (pos? i)
(recur (debug (dec i)))))
with a warning-free result:
lein test tst.demo.core
4
3
2
1
0
It looks like using just plain 5
causes the compiler to use a primitive, which can't be type hinted. Explicitly creating an Integer
object sidesteps the problem. I also tried (int 5)
which didn't work.
Is there a reason you want to turn on reflection warnings? I normally never use them, especially for debugging.
Update
Note that if you wrap the code in a function like so:
(defn stuff
[arg]
(loop [i arg]
(when (pos? i)
(recur (debug (dec i))))))
there is no problem calling (stuff 5)
since function args must always be passed as objects (via autoboxing if necessary).
Upvotes: 1