clojure-dev

Niki 2025-03-18T21:49:08.921359Z

Is this expected?

[~] clj
Clojure 1.12.0
user=> (set! *warn-on-reflection* true)
true
user=> (java.util.HashMap. {})
Reflection warning, NO_SOURCE_PATH:1:1 - call to java.util.HashMap ctor can't be resolved.
{}
user=> (java.util.HashMap. ^java.util.Map {})
Reflection warning, NO_SOURCE_PATH:1:1 - call to java.util.HashMap ctor can't be resolved.
{}
user=> (let [m {}] (java.util.HashMap. m))
Reflection warning, NO_SOURCE_PATH:1:13 - call to java.util.HashMap ctor can't be resolved.
{}
user=> (let [^java.util.Map m {}] (java.util.HashMap. m))
{}

đź‘€ 1
technosophist 2025-03-20T12:14:30.119889Z

This? https://clojure.atlassian.net/issues/CLJ-1929 (says it was fixed in 1.12)

oyakushev 2025-03-20T12:24:46.554409Z

oyakushev 2025-03-20T12:25:45.972399Z

Looks like the alternative method-type-hinting approach was deemed a proper solution for this kind of problem.

âś… 1
Niki 2025-03-20T17:08:42.396589Z

Sad :(

oyakushev 2025-03-18T21:53:23.338179Z

I'd say yes. When you use ^ syntax on a literal, the metadata is associated to the data at runtime. In the generic case, the compiler can't resolve ^something {:arbitrary data} to do it in compile time.

oyakushev 2025-03-18T21:54:04.003779Z

Since it happens at runtime, the compiler doesn't know about the type tag you assigned to the literal.

Niki 2025-03-18T21:54:11.597109Z

no, this is read-time constant, same as symbol

oyakushev 2025-03-18T21:54:39.842299Z

{} is but {:something (+ 1 2} is not.

âť“ 1
Niki 2025-03-18T21:55:17.576279Z

Yeah but I have {}

âś… 1
2025-03-18T21:55:53.293099Z

I'd have thought/assumed literal maps would already had type tags available for compiler in general as well.

oyakushev 2025-03-18T21:56:56.134109Z

I might be wrong about the reasoning. But I'm pretty sure that only symbols and lists ("round parens") can have compile-time metadata

oyakushev 2025-03-18T21:57:33.720479Z

That's one of the reasons why the compiler doesn't attach line numbers to vectors and maps

Niki 2025-03-18T21:57:36.782409Z

> I'd have thought/assumed literal maps would already had type tags available for compiler in general as well. Same

Niki 2025-03-18T21:58:28.811579Z

Even without metadata, compiler can see that it’s a map?

borkdude 2025-03-18T21:58:30.640709Z

This is weird:

user=> (set! *warn-on-reflection* true)
true
user=> (java.util.HashMap. ^java.util.Map (identity {}))
{}

oyakushev 2025-03-18T21:59:05.675729Z

> This is weird: It is not. The type tag went onto (identity {}) form.

borkdude 2025-03-18T21:59:15.846819Z

user=> (java.util.HashMap. ^java.util.Map (do {}))
Reflection warning, NO_SOURCE_PATH:1:1 - call to java.util.HashMap ctor can't be resolved.
{}

đź«  1
oyakushev 2025-03-18T21:59:35.484679Z

do gets expanded very early by the compiler, it's the "specialest" of all forms

borkdude 2025-03-18T22:00:24.739289Z

yes, it's all logical when you look into the compiler, but I can see why it's confusing to people who don't

Niki 2025-03-18T22:00:28.160899Z

Same with if, I guess? Because it all started with me trying to annotate if

borkdude 2025-03-18T22:00:59.974579Z

anyway, just use a local I guess ;)

Niki 2025-03-18T22:01:27.039919Z

wait

user=> (^[java.util.HashMap] java.util.HashMap/new {})
Syntax error (IllegalArgumentException) compiling java.util.HashMap/new at (REPL:1:1).
Error - param-tags [java.util.HashMap] insufficient to resolve constructor in class java.util.HashMap

oyakushev 2025-03-18T22:01:33.024959Z

I would be more surprised by if not retaining metadata than do, but it probably makes sense too

Niki 2025-03-18T22:02:09.026359Z

> I would be surprised > makes sense It’s one or the other

borkdude 2025-03-18T22:02:14.746399Z

user=>  (^[java.util.Map] java.util.HashMap/new {})
{}

Niki 2025-03-18T22:02:30.292389Z

Oops

Niki 2025-03-18T22:02:39.342009Z

Yeah that makes sense

oyakushev 2025-03-18T22:02:42.478519Z

> Even without metadata, compiler can see that it’s a map? The compiler doesn't have a special check and type inference flow for the case where the user passed a literal empty map, yes:)

oyakushev 2025-03-18T22:03:13.161579Z

> It’s one or the other than is operative in that sentence

Niki 2025-03-18T22:04:21.416949Z

The compiler doesn't have a special check and type inference flow for the case where the user passed a literal empty map, yes:)No, it know it’s a map. Even run-time {...} literal will produce map in the end, type of which Compiler knows

borkdude 2025-03-18T22:04:48.012459Z

Compiler knows, but doesn't use this knowledge

oyakushev 2025-03-18T22:08:37.110639Z

=> (Compiler/analyze clojure.lang.Compiler$C/EVAL '{})

#object[clojure.lang.Compiler$EmptyExpr 0x115eee10 "clojure.lang.Compiler$EmptyExpr@115eee10"]
The map is wrapped in a EmptyExpr by the analyzer. If the HostExpr analyzer were to peel this wrapper off, it would be able to use the class of the underlying object, yes. But it indeed doesn't.

Niki 2025-03-18T22:12:10.185759Z

> Compiler knows, but doesn't use this knowledge I want this on a t-shirt!

👍 1
2025-03-18T22:30:22.647539Z

hmm that has less info than i remember

2025-03-18T22:31:08.561319Z

i remember a discussion about this but it must have happened on slack

Alex Miller (Clojure team) 2025-03-18T23:19:37.931949Z

I didn’t read all this thread but we have a ticket for the issue of not being able to type hint literal colls. The places where you need to do it are uncommon but it is frustrating when it happens and I think it should be fixable. On my phone, so not going to try to find it atm

👍 2
Niki 2025-03-18T21:53:25.063499Z

And while we are on the topic, is this expected as well (can’t annotate ctors with param-tags):

[~] clj
Clojure 1.12.0
user=> (set! *warn-on-reflection* true)
true
user=> (defn f [s] (String. s))
Reflection warning, NO_SOURCE_PATH:1:13 - call to java.lang.String ctor can't be resolved.
#'user/f
user=> (defn f [s] (^[String] String. s))
Reflection warning, NO_SOURCE_PATH:1:13 - call to java.lang.String ctor can't be resolved.
#'user/f
user=> (defn f [s] (String. ^String s))
#'user/f

borkdude 2025-03-18T21:54:17.262239Z

(defn f [s] (^[String] String/new s))

Niki 2025-03-18T21:54:38.200639Z

Oh

borkdude 2025-03-18T21:54:48.162399Z

I believe the param-tags stuff is designed in conjunction with the qualified method stuff

🤯 1
âž• 1
Niki 2025-03-18T21:57:10.109079Z

Thanks, that helps solving my problem!