clojure-dev

exitsandman 2024-07-30T15:47:44.933849Z

Is this known behavior?

(clojure-version) ; => "1.12.0-beta1"

(definterface IThing
  (beSlow [^Long x])
  (beSlow [^String x]))

(def ^IThing x 
  (reify IThing
    (beSlow [_ ^Long _x] :long)
    (beSlow [_ ^String _x] :string)))

(.beSlow x 1) ; causes reflection warning
(IThing/.beSlow x 1) ; causes reflection warning
(^[Long] IThing/.beSlow x 12) ; ok
(.beSlow x (Long. 1)) ; ok
(.beSlow x "hello") ; ok
more details: • this seems to happen only when there are overloads with the same arity. • it happens with any class, I just spun up the example with definterface to be minimal • reflection calls the right overload even if there is e.g. an Integer arity and a int arity; however, if a long and a Long overload exist, the long overload is called on unboxed inputs only

👀 1
borkdude 2024-07-30T15:57:34.189529Z

FWIW beta2 is out

exitsandman 2024-07-30T16:00:50.959909Z

No changes in this behavior, but ty for the heads up

Kirill Chernyshov 2024-07-30T16:03:00.926139Z

Long is java.lang.Long to avoid reflection warning you might want ^long as an alias to primitive long

Kirill Chernyshov 2024-07-30T16:03:40.763499Z

(set! *warn-on-reflection* true)

(definterface IThing
  (beSlow [^long x])
  (beSlow [^String x]))

(def ^IThing x
  (reify IThing
    (beSlow [_ ^long _x] :long)
    (beSlow [_ ^String _x] :string)))

(.beSlow x 1) ;; => :long
no reflection warning

exitsandman 2024-07-30T16:04:24.582039Z

yeah I know, but I'm working with a type I don't own which has a Number-accepting overload. I think my best bets at the moment are wrapping the arity with a function and call that, or box manually at callsite.

seancorfield 2024-07-30T16:20:02.869989Z

@doppiaelle1999 Which part(s) of the behavior are you surprised by or think are wrong?

seancorfield 2024-07-30T16:26:18.309609Z

This has caused a reflection warning back to Clojure 1.3:

(.beSlow x 1) ; causes reflection warning

exitsandman 2024-07-30T16:28:01.593439Z

I expected the compiler to fallback to trying to resolve the overload as if it were passed a boxed Long, like it happens in this case:

(definterface IAnotherThing
  (beFast [^Long x]))

(IAnotherThing/.beFast (reify IAnotherThing (beFast [_ x] 3)) 3)

seancorfield 2024-07-30T16:28:31.253459Z

It doesn't need reflection since there are no alternatives.

exitsandman 2024-07-30T16:29:15.035279Z

yeah, ig this is why

seancorfield 2024-07-30T16:30:09.063719Z

I was a bit surprised the compiler accepted this line going as far back as Clojure 1.3 -- but it does, and also produces a reflection warning from 1.3 up to the latest:

(IThing/.beSlow x 1) ; causes reflection warning

exitsandman 2024-07-30T16:30:29.319039Z

it does because it ignores the ns haha

seancorfield 2024-07-30T16:30:51.958959Z

Ah, right. I remember that being mentioned earlier in the 1.12 cycle now...

seancorfield 2024-07-30T16:32:24.108539Z

So I guess the answer to your original question is "yes"?

exitsandman 2024-07-30T16:32:43.887559Z

guess so 🤷 Still, not much of an issue. I guess the way the beSlow case is handled is consistent with the way resolution is handled in the presence of both a boxed-accepting and unboxed-accepting overload -where the unboxed-accepting overload is called only if an unboxed value is supplied-. The thing that confuses me the most is why in the beFast case there is a silent fallback to the boxed-accepting overload, and that in the opposite situation (unboxed-accepting overload called with boxed value) reflection never arises.

👍 1
fogus (Clojure Team) 2024-07-30T17:05:07.886779Z

In the beSlow case, you're attempting to use the qualified method IThing/.beSlow . In 1.12 qualified methods must resolve to a single signature, else invocation falls back to reflection. We can use param-tags to specify the exact method to invoke, i.e. (^[Long] IThing/.beSlow x 1) (note: attempting to use long in the param-tag is not a match for Long).

1
exitsandman 2024-07-30T17:07:42.282849Z

It just was to test if the behavior was consistent with the pre-1.12.x style interop.

👍 1
seancorfield 2024-07-30T18:04:23.977729Z

None of these are reflective calls (from Clojure 1.3 to 1.12):

(definterface IThing
  (boxed [^Long x])
  (primitive [^long x]))

(def ^IThing x
  (reify IThing
    (boxed [_ ^Long _x] :boxed-long)
    (primitive [_ ^long _x] :primitive-long)))

(.boxed x 1)
(.primitive x 1)
(.boxed x (Long. 1))
(.primitive x (Long. 1))
Because there's only one choice for each method and auto-boxing / auto-unboxing is used in both directions.

exitsandman 2024-07-30T18:25:23.897009Z

yeah, the behavior only appears to come up when the signature can't be resolved by method name and arity alone

seancorfield 2024-07-30T18:37:26.166549Z

Right. Which is to be expected (at least, for me).