Fork me on GitHub
#beginners
<
2024-06-12
>
stopa17:06:36

Question on type-hinting arrays: I am trying to call newMemberOverload with java interop. In order to avoid reflection warnings, I wrote the following:

(def ^MapType type-obj (MapType/create SimpleType/STRING SimpleType/DYN))

(def ^ListType type-ref-return (ListType/create SimpleType/DYN))

(CelOverloadDecl/newMemberOverload
 "data_ref"
 type-ref-return
 ^CelType/1 (into-array CelType [type-obj SimpleType/STRING]))
This is my first type going deep in on type-hinting. I wanted to double check: Is the correct / concise way to typehint? Can I write this in a more concise way? I was surprised I had to type-hint CelType/1, even though I had written into-array https://github.com/google/cel-java/blob/main/common/src/main/java/dev/cel/common/CelOverloadDecl.java#L165-L169

Alex Miller (Clojure team)17:06:13

looks good. because into-array can return any kind of array it can't be statically type hinted

Alex Miller (Clojure team)17:06:45

keep in mind that you don't need to type hint everything, just things that are ambiguous, let reflection warnings be your guide

❤️ 1
Alex Miller (Clojure team)17:06:13

(these may all be needed, don't know)

hiredman17:06:06

def's are also going to be the most verbose when it comes to hinting, let bindings can infer their type from the expression in many interop cases, but defs don't. the hint on type-obj is not needed for the shown code because into-array is not strictly speaking an interop call

👍 1
hiredman17:06:26

my procedure for adding type hints to a namespace is: 1. make sure (set! *warn-on-reflection* true) is right after the namespace form 2. load the file in the repl (either load-file or require with :reload) 3. look at the location of the last reflection warning (at the bottom) 4. fix it by hinting the indicated expression at the use site, or if close by hint at the binding site (let binding or function parameter sort of things) a. sometimes fixing is a little more involved than adding a single hint b. maybe use an existing function that has the right hint already like str 5. goto to #2

❤️ 1
hiredman17:06:05

that tends to bias towards hinting at use sites over definition sites

stopa20:06:13

Love the deep dive and the procedure. Thank you team!

Ludger Solbach23:06:55

I have :global-vars {*warn-on-reflection* true} in my leiningen user profile so they are always on in development. I suppose you can do this in deps.edn too.

stopa21:06:56

One more typehint question. I am using a proxy to create a special kind of AbstractMap:

(ns stopachka.typehint)

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

(defn ->special-map [m]
  (proxy [java.util.AbstractMap] []
    (get [k]
      (get m k :special-nil))
    (containsKey [k]
      true)
    (entrySet []
      (.entrySet (or m {})))))
I see a reflection warning on entrySet:
; (err) Reflection warning, 
reference to field entrySet can't be resolved.
To solve this, I tried adding a type-hint like so:
(defn ->special-map [m]
  (proxy [java.util.AbstractMap] []
    (get [k]
      (get m k :special-nil))
    (containsKey [k]
      true)
    (entrySet []
      (.entrySet ^java.util.Map (or m {})))))
But alas the reflection is still happening. Any thoughts much appreciated!

seancorfield21:06:33

This may be because you're proxying java.util.AbstractMap but if you're on Clojure 1.12, you could use (java.util.Map/.entrySet (or m {})) to avoid the reflection.

seancorfield21:06:05

For earlier versions of Clojure, I think this will work:

(let [^java.util.Map m' (or m {})] (.entrySet m'))

stopa21:06:53

java.util.Map/.entrySet worked like a charm! Thanks @U04V70XH6

ghadi21:06:10

right, the compiler cannot infer across branches, and even if it could, m isn't known

seancorfield21:06:16

But ^java.util.Map (or m {}) doesn't work because...? I would have expected the type hint to work there...

ghadi21:06:56

there's a jira for that

seancorfield21:06:22

Oh, OK. So it's a known limitation... because the hinted expr is a macro call?