Fork me on GitHub
#releases
<
2022-01-16
>
ericdallo18:01:22

Released clj-easy/stub https://github.com/clj-easy/stub, a lib for generating stubs for your project/classpath. This release just improve the jar removing unused deps along with make available the jar + standalone jar used on native image

🎉 4
Eddie17:01:58

This looks awesome, thanks for your work! Can stub be used to trick editors (specifically Cursive) into recognizing dynamically defined def/defns? For example:

(when i-want
	(def x 1))

borkdude17:01:40

@U7XR2PZFW It depends if your runtime defines those vars.

borkdude17:01:29

for static analysis you might just want to write (def x (when i-want ...))

Eddie17:01:25

I'm not sure I understand what you mean by "runtime" in this context. The typical scenario where I find myself wanting dynamically bound vars is when proxy-ing a java/scala library. If the library version is newer than ver, then we want to bind clj-foo to a proxy of .javaFoo, otherwise we want to skip defining the clj-foo var entirely because .javaFoo doesn't exist yet. I would naturally want to write this as:

(when (> (detect-lib-version) ver)
	(defn clj-foo [obj]
		(.javaFoo obj)))
but I have sometimes avoided this because Cursive won't resolve calls to clj-foo. Some popular libraries use this same pattern and https://cursive-ide.com/userguide/macros.html#stub-generation using the editors classpath but there is doesn't seem to be a way for users to extend this functionality to include additional namespaces.

borkdude17:01:50

The above will still define the var clj-foo since defn is macro which during macro expansion creates a var

borkdude17:01:02

just try it out. (when false (def x 1)) x

Eddie19:01:49

but when is also a macro, no? I get #object[clojure.lang.Var$Unbound 0x2e554a3b "Unbound: #'user/x"] so I see your point.

Eddie19:01:31

I think in my desire to create a minimal example, I ended up removing the behavior I was trying to demonstrate.

Eddie19:01:29

Let me think/read about this some more. I was trying to achieve a Unable to resolve symbol: x in this context when the condition was false.

Eddie20:01:17

I think I see where I got mixed up. It seems like macro expansion expands the inner-most forms first. Is that right?

Eddie20:01:49

Regardless, we can make a macro that optionally does (not) contain any var definitions.

(defmacro defn-when
    [cond sym args & body]
    (when (eval cond)
      `(defn ~sym ~args ~@body)))

(macroexpand
  '(defn-when true f [x]  (inc x)))
;; => (def f (clojure.core/fn ([x] (inc x))))

(macroexpand
  '(defn-when false f [x]  (inc x)))
=> nil

Eddie20:01:01

Sorry for straying from the topic of the thread. I'll see if I can find some time to create an example project where Cursive fails to resolve a var and cannot generate stubs itself. Then I will see if I can point this tool at the Cursive classpath and see if anything useful happens 🙂