Fork me on GitHub
Sam Ritchie03:02:18

is there a way to see the namespace that is currently being linted, from a hook? I am looking to suppress this error:

test/sicmutils/fdg/ch10_test.cljc::: warning: redefined var #'sicmutils.fdg.ch10-test/spherical
which happens because this form:
(define-coordinates [r theta phi] spherical)
expands into:
(let* [G__57649 (with-coordinate-prototype spherical ['r 'theta 'phi])]
  (def spherical G__57649)
This overwrites the spherical binding, of course, but that’s the intent of this form. So my thought was that my hook could just NOT emit the def if the symbol is already bound in the namespace. But maybe there is a better way to suppress?


Check all the keys in the hook arg, it nay already be in there

Sam Ritchie03:02:50

@borkdude ugh, I printed the whole thing before and didn’t see :namespace but of course it’s here as :ns. I’m making a strong effort to not waste your time here, I promise!!

Sam Ritchie03:02:48

@borkdude but while I have you, I was thinking that one nice function in a hook would be something like fmap ; the pattern of (api/token-node (f (:value node))) , (api/list-node (f (:children node))) etc would be instead (api/fmap f node) in both cases

Sam Ritchie03:02:22

powered by a protocol offering fmap* or something so you could correctly dispatch on the non-function arg

Sam Ritchie05:02:51

@borkdude it won’t quite work due to caching, I think. here is what I tried:

(defn define-coordinates [{:keys [node ns]}]
  (let [[_ _ system] (:children node)
        analysis (api/ns-analysis ns)
        existing (into (set (keys (:clj analysis)))
                       (keys (:cljs analysis)))
        sys-name (api/token-node (symbol (name (:value system))))
        new-node (api/list-node
                   [(api/token-node 'do)]
                   (when-not (existing (:value sys-name))
                       [(api/token-node 'def) sys-name system])])))]
    {:node new-node}))
the first time I run the linter, given some form like
(define-coordinates t e/R1-rect)
the linter runs with no warnings (but internally (def R1-rect e/R1-rect) is emitted). So next time I run the linter, (api/ns-analysis ns) shows that the binding exists, since the linter emitted it last time… which means that this form will NOT be present the second time, causing error: Unresolved symbol: R1-rect farther down in the file


Why is this linting conditional on things existing prior to it?

Sam Ritchie13:02:56

My intent is to be able to let the user write:

(ns sicm.example
  (:require [sicmutils.calculus.manifold :refer [R2-rect]]))

(define-coordinates [x0 y0] R2-rect)
and not trigger warning: R2-rect already refers to #'sicmutils.env/R2-rect ; so this was my guess at how to do that, by checking what existed in the incoming state and reacting. (you could argue that it IS a good warning and the user should absolutely not do a bare R2-rect import then overwrite it, and I AGREE… except that to be compatible with all of the scheme code this is mirroring, the user gets an environment with everything exposed, then can rebind stuff selectively with define-coordinates. The reason you’d do that is basically to give the coordinates names, like x y z.)

Sam Ritchie13:02:23

@borkdude another way to handle this would be

#_{:clj-kondo/ignore [:redefined-var]}
(define-coordinates [x0 y0] R2-rect)
but strangely that doesn’t seem to kill the warning. it DOES if I paste in the macroexpansion directly:
#_{:clj-kondo/ignore [:redefined-var]}
(let* [G__43077
       (sicmutils.calculus.manifold/with-coordinate-prototype R2-rect ['x0 'y0])]
  (def R2-rect G__43077))


why not (declare R2-rect) ?

Sam Ritchie13:02:40

generate that inside the hook, you mean?


yeah, then that won't trigger a redefined var warning

Sam Ritchie13:02:26

interesting, that almost works, and the reason why not is because I didn’t think of it 🙂

Sam Ritchie13:02:13

almost because now we get this:

#'sicmutils.env/R2-rect is referred but never used
(ns sicm.example
  (:require [sicmutils.env:refer [R2-rect]]))

(define-coordinates [x0 y0] R2-rect)


you can just generate another usage after that

Sam Ritchie13:02:02

got it, so generate all the declares, and also a vector of all of the syms

Sam Ritchie13:02:36

awesome, I’ll try that soon. thanks!


or you can generate something like potemkin import-vars which will then treat those defined vars as the imported vars with respect to arity, etc

Sam Ritchie13:02:02

I’m getting the hang of this. next @borkdude is to go document my hooks well and emit more custom warnings etc from the body


since there is built-in support for potemkin

Sam Ritchie13:02:55

@borkdude is it the case that to make that work, I’d again be in the position of having to know which ones were being declared for the first time and which were overwriting?


yeah I think so

👍 1
Sam Ritchie03:02:52

is it possible to access other namespaces than hooks-api in hooks? I think no; but for this particular task, I think I need unquote-node and unquote-splicing-node

Sam Ritchie03:02:58

(let [seq-node? (some-fn api/list-node? api/vector-node?)
        atom? (some-fn api/token-node? api/string-node? api/keyword-node?)
        tree (tree-seq seq-node? :children node)]
    (prn (remove (some-fn seq-node? atom?) tree)))

Sam Ritchie03:02:10

here is my “isolate via exclusion” method 🙂

Sam Ritchie04:02:44

potential bug…

(even? ('a {'a 10}))
triggers an “Insufficient input” error… I’m not sure why, and I am convinced this is correct (the call evaluates)


This is a bug. You can ignore it with:

(even? ('a {'a 10}))

Sam Ritchie06:02:15

Here is an example of a potential bug with :redundant-fn-wrapper:

(map (fn [x]
       {:pre [(odd? x)]}
       (- x))
     (range 10))
this triggers, but I would argue that it is NOT redundant because it adds a precondition. Thoughts?


Also a bug. Ignore it with:

(fn [x]
  {:pre [(odd? x)]}
  (- x))
Thanks for finding these!

👍 1
Sam Ritchie13:02:18

@borkdude all 52k lines of tests + source from sicmutils linted, and all warnings and errors resolved! This project is amazing… I’m kicking myself for not enabling clj-kondo sooner. I’ll get an exact count when I polish the PR, but this process exposed a number of bugs that would have been damned hard to track down without the linter. Woohoo! Also I’m realizing that it’s such a pleasure to be able to write hooks that guide users toward correct usage of more difficult stuff like the pattern matching API. THANK YOU!

🎉 8