clojure-dev

2025-10-31T18:53:02.282569Z

Has it been considered to enhance the compiler to give special priority to Object methods when it comes to method resolution? Here's a common one, I can't think of a case where Object/.getClass would be ambiguous (yet). It looks like even defining a field called getClass is ignored.

user=> (deftype Foo [getClass])
user.Foo
user=> (#(.getClass ^Foo %) (->Foo 1))
user.Foo
In particular, I'm interested in removing the need for ^Foo here, because (I hypothesize) it's unambiguous that .getClass == Object/.getClass Other methods like Object/.equals might be even less ambiguous because it takes an argument, and hence has distinct syntax to Object/.-equals .

oyakushev 2025-11-01T11:59:47.856809Z

Keep in mind that due to possible future Valhalla changes, eventually not everything might be an Object. This likely won't happen due to other introduced incompatibilities, but still.

πŸ‘ 1
2025-11-03T17:51:59.243039Z

Great point, that takes the wind out of the sails of this idea for me. It's only enhanceable if we assume the reflector will always box the target to an Object. I'll share two possible further lines of investigation: β€’ (.getClass 1) reflects, should it? β€’ clojure-clr has a subtle difference in its reflector in that it prioritizes fields over zero-arity methods https://clojurians.slack.com/archives/C060SFCPR/p1762191030298889?thread_ts=1761942749.095129&cid=C060SFCPR

borkdude 2025-10-31T18:55:49.254929Z

Phrased differently: why do we have to type hint .getClass calls here:

user=> ((fn [x] (.getClass x)) 1)
Reflection warning, NO_SOURCE_PATH:1:10 - reference to field getClass can't be resolved.
java.lang.Long

βž• 2
Alex Miller (Clojure team) 2025-10-31T19:11:00.420419Z

I think the answer to the question up top is, no. I suspect the answer to borkdude's question is probably that there is a difference in some cases between "type unknown" and "type = Object"

πŸ‘ 1
Alex Miller (Clojure team) 2025-10-31T19:17:36.967189Z

I'm pretty sure there are multiple tickets related to this. quick search found https://clojure.atlassian.net/browse/CLJ-1688 for example

2025-10-31T19:21:11.835919Z

Whoops, thanks for finding those. I think we need to answer the ambiguity question before proceeding, I'll add it to the pile.

borkdude 2025-10-31T23:14:47.918139Z

Is there a reason why args is reassigned here? https://github.com/clojure/clojure/blob/6a4ba6aedc8575768b2fff6d9c9c7e6503a0a93a/src/jvm/clojure/lang/Reflector.java#L178 @ambrosebs and I are porting reflector code in SCI over to clojure so it's easier to port to ClojureCLR. By accident we didn't re-assign args but used the old args and this actually works fine. Since the args go through box-args I think it will actually just work out but perhaps I'm missing something

Alex Miller (Clojure team) 2025-11-01T14:44:21.602819Z

This is for a specific case in the last commit there - you can find it in the git blame

Alex Miller (Clojure team) 2025-11-01T14:45:38.512109Z

So yes, as far as I recall it’s important to reassign there

borkdude 2025-11-01T14:52:00.127199Z

If I'm not misunderstanding, the widened args help finding the right method but after that I think you could just use the old unwidened args? At least, this works for me

(ns reflect-example
  (:import [java.lang Thread Integer]))

(def m (.getMethod Thread "sleep" (into-array Class [Long/TYPE])))
(.invoke m nil (into-array Object [1]))
(.invoke m nil (into-array Object [(Integer/valueOf 1)]))

borkdude 2025-11-01T14:53:43.542789Z

(all 106 tested lib tests in bb CI passed without re-assignment, but there could of course be something I'm overlooking)

borkdude 2025-11-01T14:55:56.131129Z

from javadoc: > A Method permits widening conversions to occur when matching the actual parameters to invoke with the underlying method's formal parameters, but it throws an IllegalArgumentException if a narrowing conversion would occur.

borkdude 2025-10-31T23:17:29.988019Z

Perhaps it's more expensive to take a doubleValue from a float twice rather than taking a doubleValue from a float and then calling doubleValue on that?