One of the new features in Clojure 1.10 is tap. The changelog describes it as follows:
tap is a shared, globally accessible system for distributing a series of informational or diagnostic values to a set of (presumably effectful) handler functions. It can be used as a better debug
prn, or for facilities like logging etc.
tap>sends a value to the set of taps. Taps can be added with
add-tapand will be called with any value sent to
tap>. The tap function may (briefly) block (e.g. for streams) and will never impede calls to
tap>, but blocking indefinitely may cause tap values to be dropped. If no taps are registered,
tap>discards. Remove taps with
- The output may go either to the Boot terminal or to the Cursive IDE depending on the code path.
- The output is not pretty-printed.
Tap allows me to solve both problems. I want my debug prints to always appear in Cursive’s REPL, so after starting the REPL, I add a tap handler by running this command:1
(add-tap (bound-fn* puget.printer/cprint))
Here bound-fn* ensures that
the output goes to Cursive and not to the terminal. Puget is the
pretty-printer I’m used to, but you can replace it with your favorite printer.
If you do not want to add new deps, you can use
clojure.pprint or even plain
(add-tap (bound-fn* clojure.pprint/pprint)) (add-tap (bound-fn* prn))
Now when I want to debug-print something, I do
(tap> "hello world"). Since
add-tap are in
clojure.core, I don’t need to require
anything. I can just
Another debugging trick is to store the tapped value in an atom. I’ve used this only once so far, but it was pretty handy. Setup:
(def debug-a (atom nil)) (add-tap #(reset! debug-a %))
Now I can
tap> an intermediate value in the middle of some complicated code
and then poke at it in the REPL via
@debug-a. Ideally you’d use
a debugger, but if you are in hurry, maybe tap is enough.
I don’t know how to do this automatically. If somebody knows, please tell me. ↩︎