To begin with, I’m a very happy Clojurian, but not when I’m working with interops. I’m using Google apis extensively for some features in my product; and so far the experience has been quite awful. I’ve been contemplating it for a while and here are my pain points.
- It is hard to look up for what method a java object supports.
- It is hard to understand the inheritance and polymorphism designs without actually looking at the java codes.
- 1,2 are amplified all the more because I’m using calva with vscode. The IDE’s java support is not as good as that of intellij.
How do you work with interops in general? I welcome any tips/advices/know-hows.
When I started Clojure 3 years ago I had zero Java and Clojure knowledge but was experienced in C, Python, Shell etc. WhileI had no issues learning Clojure I did struggle with Java in Clojure, where Java by itself wasnt an issue.
I use VSCode with Calva aswel.
Clojure.reflect helps a lot and so does bean.
I made this snippet in a dev ns to require and use to get methods: (ns myapp.dev (:require [clojure.pprint :refer [print-table]] [clojure.reflect :refer [reflect]]))
(defn get-members [in] (print-table [:name :flags :parameter-types :return-type] (sort-by :name (:members (reflect in)))))
And I got this helper from a gist: (comment “call private methods from https://gist.github.com/egamble/7781127”)
(defn call-method [obj method-name & args] (let [m (first (filter (fn [x] (… x getName (equals method-name))) (… obj getClass getDeclaredMethods)))] (. m (setAccessible true)) (. m (invoke obj (into-array Object args)))))
I personally use Emacs + CIDER inspector
Can you say a bit more about your actual workflow? I also use CIDER and I’ve never found completion of navigating javadocs to be great. But I suspect I just don’t know the right commands/functions.
Sure. Basically:
- I eval stuff- Inspect the result via cider-inspector
- Drill into the java class via `cider-inspector-operate-on-point`
- See all the class method signatures
Oh! The literal inspector - I had no idea it had that capability. Amazing.
Happy Friday, I will dig into this.
PS. Nice video, seeing is believing.
Yeah it’s pretty amazing. I can’t live without it
IntelliJ IDEA is definitely much better than VSCode in this regard, and it’s pretty much the only thing that’s stopping me from switching to VSCode.
But if you’re keen on keeping using VSCode, then IMO looking at the sources written in Java is your best bet. In addition, you can use Java reflection, directly or via
clojure.reflect/reflect
. In the case of thereflect
function, things like Portal would be useful since even the simplest types will have a lot of members and Portal will help you with navigating the resulting data structure.Agree with you on vscode. I’ve been using intellij for quite a while but switched to vscode for various reasons (one of them being vscode’s deps prompt when starting repl. It works well with polylith architecture).
About reflect, I made some utility functions with limitations. The thing is reflect does not show inherited methods and I have no way of knowing the full capabilities of an object. And does portal allow navigation in and out of Java object? When I tap> whatever is bound to portal it only shows a shallow data, not object, in portal screen.
Yeah, getting to inherited methods requires more work, more utility functions. But you can still get that information since you have the info on the bases of a class - you can reflect the whole base tree and build up the full set of supported methods.
- IntelliJ plus type hinting code gives method lookup popups:
(defn foo [^^GoogleFactoryProxy factory-proxy] (doto (.fooMethod factory-proxy) (.addStuff (stuff))))
doto is great for functions returning void. You can still use threading macro if the Java object returns self.
Method hints pop up after the first dot.
If you need to implement interfaces or extend objects then I would write wrappers which do the reify work and pass function arguments to those wrappers. Otherwise the code becomes noisy and has less signal.
Honestly, I have found that extending objects is just easier in Java. The clojure interface for that is generally not worth the effort.
So it is intellij after all. Is it your primary IDE?
My primary is neovim, but for interop I use intellij.
IntelliJ is primary along with Vim plugin, clj-kondo and paredit for retaining some sanity while editing. There is some jankiness with clj-kondo but all in all I would say the REPL integration is on par with emacs. Never had the patience to set up vim or dabble with neovim when IntelliJ has it all out of the box.
IntelliJ even supports prefix key bindings now which allow binding repl functions and custom keybindings behind a prefix key.
For example as `ctrl-x` follow by `e` sends the last expression to the repl.
I generally rely on documentation + REPL.
I use this code in my REPL to explore unfamiliar / poorly documented Java codebases: https://gist.github.com/pmonks/223e60def27266e79fff47de734d060a