Fork me on GitHub

Released 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

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))


@U7XR2PZFW It depends if your runtime defines those vars.


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


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 using the editors classpath but there is doesn't seem to be a way for users to extend this functionality to include additional namespaces.


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


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


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


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


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.


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


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)))

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

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


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 🙂