How I use tap>

A shadow against a bridge pillar on a rock.

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-tap and 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 remove-tap.

I’m already using it as a better debug prn! I’m using Cursive and I connect to a REPL launched by Boot. With my setup, (prn :DEBUG value) has two potential downsides.

  1. The output may go either to the Boot terminal or to the Cursive IDE depending on the code path.
  2. 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 old prn:

(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 both tap> and add-tap are in clojure.core, I don’t need to require anything. I can just tap> away.

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.

  1. I don’t know how to do this automatically. If somebody knows, please tell me.

Comments or questions? Tweet to me or send me an e-mail.