Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sogaiu/fbf4a1f4aa2a8311b9612ae7460beb65 to your computer and use it in GitHub Desktop.
Save sogaiu/fbf4a1f4aa2a8311b9612ae7460beb65 to your computer and use it in GitHub Desktop.
How to Learn About Typical Argument Values to Some Functions in Clojure >= 1.10

How to Learn About Typical Argument Values to Some Functions in Clojure >= 1.10

Background

I was completely in the dark about what sorts of values are used as arguments to extract-bindings in clj-kondo's analyzer.clj. I didn’t find any docs for the function in question, but this was not surprising because the project is quite new and the function is an implementation detail. There was, however, a test for it, so that was a start. However, one is not always blessed with such code and sometimes it is desirable to see more "real-worldy" examples.

Overview

The following describes one fairly general method to become more familiar with typical values for arguments to a function — at least, one for which an indirect triggering method is known or can be determined.

The idea is to:

  • Modify the function-to-examine to log its argument values via tap> and have these sent to REBL (as examining logged values that are large / complex directly at the repl can be unwieldy).

  • Call a function one does know how to call that will eventually lead to the function-to-examine being called.

  • Use REBL’s GUI to examine the logged data.

Figuring out how to indirectly trigger the function one wishes to learn more about may require some research. For the current example, it turns out that there is a convenient entry point that will eventually trigger the function-to-examine.

Details

0. Ensure the local system has a jar for REBL (e.g. saved as ~/src/REBL/REBL.jar) and ~/.clojure/deps.edn has an alias for REBL, e.g.

:rebl
{
 :extra-deps
 {
  org.clojure/clojure {:mvn/version "1.10.0"}
  org.clojure/core.async {:mvn/version "0.4.490"}
  org.clojure/data.csv {:mvn/version "0.1.4"}
  org.clojure/data.json {:mvn/version "0.2.3"}
  org.yaml/snakeyaml {:mvn/version "1.23"}
  com.cognitect/rebl {:local/root "../REBL/REBL.jar"}
 }
}

See Sean Corfield’s excellent sample ~/.clojure/deps.edn for more.

1. Clone clj-kondo:

$ cd ~/src && git clone https://github.com/borkdude/clj-kondo && cd clj-kondo

2. Edit src/clj_kondo/impl/analyzer.clj’s extract-bindings to start like this:

(defn extract-bindings
 ;; added next line
 ([ctx expr a b] (extract-bindings ctx expr a))
 ([ctx expr] (extract-bindings ctx expr false))
 ([ctx expr keys-destructuring?]
  ;; also added the next line
  (tap> {:ctx ctx :expr expr :keys-destructuring? keys-destructuring?})

N.B. there are 2 changes:

  • Added an extra arity to cause a warning to be generated by clj-kondo. (We’re going to run clj-kondo on itself, and it doesn’t typically lead to warnings when run that way, so we make it "dirty" on purpose.)

  • Added a call to tap> to log argument values.

3. Start a repl with the REBL alias:

$ clj -A:rebl -r
Clojure 1.10.0
user=>

4. Load REBL and start its GUI:

user=> (require '[cognitect.rebl :as cr])
nil
user=> (cr/ui)
nil

5. Hook up REBL to tap>:

user=> (defn to-rebl [m] (cr/submit (System/currentTimeMillis) m))
#'user/to-rebl
user=> (add-tap to-rebl)
nil

6. Load clj-kondo:

user=> (require '[clj-kondo.core :as cc])
nil

7. Use clj-kondo via its Clojure API:

user=> (def rel-path "src/clj-kondo/src/clj_kondo/impl/analyzer.clj")
#'user/rel-path
user=> (def abs-path (str (System/getenv "HOME") "/" rel-path))
#'user/abs-path
user=> (clojure.pprint/pprint (cc/run! {:lint [abs-path]}))
{:findings
 ({:level :warning,
   :type :unused-binding,
   ;; edited
   :filename ".../src/clj-kondo/src/clj_kondo/impl/analyzer.clj",
   :message "unused binding b",
   :row 47,
   :col 16}),
 :config
 {...}, ;; edited
 :summary
 {:error 0, :warning 1, :info 0, :type :summary, :duration 290}}
nil

8. Use the REBL GUI to examine the logged values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment