A summary of techniques to debug Clojure code via REPL (aka “the interactive console”, the “read-eval-print loop”) and Cider debugger.

Just print

As a beginner, it’s not clear how to print things from awkward points like inside of a (let [...]) binding declaration, or a (-> ...) threading expression. My initial journey landed on this article REPL Based Debugging in Clojure. It’s an easy start.

Fundamentals: the clojure.org docs

Enhancing your REPL workflow is a must read! You can skip the intro, straight to the Debugging section. Read about inspecting and logging with libraries like spyscope and clojure.tools.logging.

Reproducing the context of an expression” is extremely useful when doing REPL development & debugging. Often times you work on a section of a function body. Evaluating that section alone will throw unable to resolve symbol. The “fix” consists of simply defining the intermediary variables that are expected in that section. I discovered this technique accidentally and was wondering if it’s fair game. When the clojure docs sanctify it, I guess it’s fair.

More to read: inline-def debugging and repl debugging: no stacktrace required. Finally, try not to skip the “Community Resources” section.

Data vizualization at the REPL goes over some more REPL tricks:

  • *e is bound to the last thrown error. Print a stacktrace with (pst *e)
  • *1, *2, *3 are bound to the last recent results in the REPL. Save for further use with (def foo *1)
  • Reflection used to inspect mysterious values
    • (type v) and (ancestors (type v)) show the type and the class hierarchy.
    • clojure.reflect like (reflect/reflect (type v)).

The debugger: Cider

Cider is an Emacs debugger for Clojure. Highlighting some workflows from “using the debugger” section in the cider docs.

Instrument a defn with C-u C-M-x. All refs to the function will be highlighted now. The debugger will break when calling the instrumented function.

Figure 1: C-u C-M-x with cursor inside pairs-contained? function

Figure 1: C-u C-M-x with cursor inside pairs-contained? function

Once instrumented, invoking the debugger on the first (assert) statement breaks and you’re dropped into the Cider debug menu.

Figure 2: C-M-x with cursor right on top of (assert ...) leading paren

Figure 2: C-M-x with cursor right on top of (assert ...) leading paren

To remove instrumentation, re-evaluate the defn.

Figure 1: ,ef within the function body

Figure 1: ,ef within the function body

Alternatives

  • place #break in front of the statement you want to break on, eg: (#break map foo [1 2 3])
  • C-M-x debug at point

Evergreen authors

Aphyr: https://aphyr.com/posts/319-clojure-from-the-ground-up-debugging. Recommend going through the whole series on “Clojure from the Ground Up”.

Aphyr has done amazing work analyzing consistency properties on numerous popular database systems here: https://jepsen.io/analyses.

Eli Benderski: https://eli.thegreenplace.net/2017/notes-on-debugging-clojure-code/.

  • excellent article! on point about the main difficulties of debugging clojure code

  • (pst) to print stacktrace for last error

  • “Learning to map from Clojure values and types to the JVM’s expectations will take time and grit - especially if you (like me) don’t have much Java experience. I suggest doing a bit of reading on Clojure/Java interoperability, and about other Java-isms Clojure inherits; it ain’t pretty, and you may not always want to use it, but being familiar with the terms can go a long way in deciphering cryptic stack traces.”

  • (trace-forms (foo bar)) https://github.com/clojure/tools.trace#example-usage

    • “very useful when errors manifest as exceptions. Unfortunately, this isn’t sufficient for all cases”
  • (trace-vars (...))

  • diy Macros “I find that debuggers are even less useful in Clojure than in other languages. On the other hand, Clojure’s macros make it possible to trace / print stuff in a very nice way.”

I knew Eli’s blog from his series of articles on the Go compiler which I loved reading while digging through the code.

Extra refs

http://www.futurile.net/2020/05/16/clojure-observability-and-debugging-tools/. Part of a series, goes over a few popular observability libraries in clojure, with examples. There are good references at the end of the article.

Fin

Will add more content as I progress.