This. With the strong interop story that Clojure has, looking up docs and signatures, while using it, is super convenient.
As for the whole initiative. Love it! Let me throw in some inspiration:
Iāll see your inspiration, and Iāll raise you one. Bozhidar says:
There are two potential classes that implement the
toUpperCasemethod. One isjava.lang.Stringand the other isjava.lang.Character. We cannot possibly know which one you are trying to evaluate here. ⦠Itās not ideal, but it simply cannot be done in any other way.
Ummm⦠in the example, heās literally calling it with a String receiver. Hereās Cursive:
I hear you say: but thatās a trivial example, and real code doesnāt look like that! Ok:
Cursive runs local type inference in the editor, mimicking what the Clojure compiler does. This allows you to have almost Java-level completions. The main issue is that in Clojure, when youāre typing out your code, the method comes before the receiver. No problem, you can do this:
Put the receiver first and use completion for your interop, and since Cursive knows the type of the receiver, youāll only get completions relevant for that type. When you actually complete the method, Cursive will swap them around for you:

You donāt need to do that if your code is naturally structured in a way which allows the type to be determined:
Since the receiver comes first in the threading form, Cursive knows its type with no swapping required.
Additionally, note that neither List nor Iterator are imported in this ns, but Cursive knows their types due to the inference. If you want to know the inferred type at any time, you can just ask:
Thereās plenty more, I could go on⦠this all works for Kotlin and Scala code too, you can rename Java/Kotlin/Scala methods from your Clojure code, finding usages of the JVM methods will find the usages in your Clojure code and vice versa, etc etc.
Thereās lots that can be done in this space!
Thanks for sharing Colin, thatās really cool. Clj-kondo has a basic form of type inference too that could be leveraged here. Still contemplating which route to go for Java bytecode/source analysis.
I also think just listing out the completion of all possible types grouped by each type would be great.
Like Iām smart enough to quickly find the section of the type I know I have. Bonus point if itās ordered by child-most type.
One question, what it it came from a global or a function parameter that had a type hint, would completion also work then?
Yes, it does. The subs example above knows about the type because subs has a type hint saying what it will return. Local function arg hints and local binding hints also work, as well as various places where things are implicitly type hinted (e.g. this args in reify/deftype/extend-type method implementations).
Ok, maybe Iām pushing my luck, but how far does this the inference goes?
Example 1:
(defn hello
[]
"hello"
(.toUpperCase (hello))
No type hint on hello, but local inference of hello should know the type from the literal and infer the return type of hello. Is that then able to be used external to the function by the call to .toUpperCase on (hello)
Example 2:
(defn hello
[name]
(str "hello " name))
(.toUpperCase (hello))
Would clojure.core functions, even though not type hinted, have their common types be known by Cursive magically (probably hard-coded somewhere)
Example 3:
(defn make-info
[^String name]
{:name name})
(.toUpperCase (:name (make-info)))
This one might be a tougher one, but basically can it infer the type of values on maps? Like would it know this is a Variant [^Keyword :name ^String name] and then know that the Keyword fn :name returns the value of key?
No, this inference currently only does what Clojure itself does. I could potentially do more, but it would only be in order to suggest to the user where they might want to add further type hints. Cases like the function return types are relatively easy to implement if I decide to go that far. The map value one is probably going beyond what would be worth it, though.
Would clojure.core functions, even though not type hinted, have their common types be known by Cursive magically (probably hard-coded somewhere)
Currently Iāve avoided magic hard-coding, but most core functions (e.g. str) are properly type hinted for interop purposes anyway.
So awesome!
Just to clarify what I meant - in CIDERās case, as weāre 100% REPL-powered (no static analysis at all), we canāt know the type of the receiver unless itās a literal or we have evaluated it. As type method hints come solely from resolving the method names without any context (we donāt send the whole expressions to the backend, just the method symbol) weāre forced to do some guesswork. We may consider adding some context down the road, at least for the trivial cases, but evaluating receivers is dangerous and potentially slow, so thatās definitely one limitation of our approach, at least for the Java interop. Clearly thatās not an issue for Clojure code, but on the Java front Cursiveās approach is way superior, thatās undeniable.