This is a weblog about computing, culture et cetera, by . Read more.


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.


Clojure libraries I recommend

Brown maple leafs on the ground.

Every now and then I see people asking what Clojure and ClojureScript libraries they should be using. In this post, I’ll share my list of libraries for building full-stack Clojure/ClojureScript web applications. I’ve been building this kind of applications for a while now and I believe you can rely on these libraries.

Here’s what I’m looking for:

This list is not meant as the final truth: there are plenty of good libraries out there and you might choose different ones based on your needs and preferences. There’s an obvious bias, too: many of these libraries were made by my colleagues at Metosin.

Clojure backend

Framework. I appreciate the data-driven nature of Integrant. Combine it with Integrant-REPL for reloaded workflow.

JDBC database connection pool. I’ve never had any problems with HikariCP and it’s easy to configure and instrument. hikari-cp is a handy Clojure wrapper.

Configuration. I use Maailma and I’ve written about how to use it. cprop is very similar and probably a bit more powerful.

HTTP routing. I like the data-driven nature of reitit. It’s fast, too, and has enough extension points so that anything you want is possible. I would not use compojure-api because the implementation is way too hard to understand.

Logging. I use Timbre because it’s so easy to write custom appenders for it. Admittedly I haven’t checked out the popular Java options.

Test runner. Eftest is the test runner of my dreams and you can’t go wrong by using it. At some point I want to give Kaocha a go.

Test assertion library. I like testit and Juxt’s iota. They’re adequate, but I think there’s room to do better here.

Test data generation. I only just started using specmonstah for generating graphs of test data, but I’m excited. It’s so much nicer than writing the equivalent code by hand. There’s a bit of learning curve, though.

ClojureScript frontend

Build tool. Figwheel works very well and its getting better all the time. I’d either use it with Leiningen or directly with clj.

Front-end framework. re-frame is the way to go. Check out re-frame-10x for debugging.

Translations. Tempura works for me. I use a small Clojure tool for extracting the translatable strings into a PO file for editing with Poedit. The tool is proprietary right now but I hope to open source it.

General tools

clojure.spec helpers. Expound makes the spec error messages human-readable and Orchestra instruments the function return values.

Data manipulation utilities. I use the combination of Potpuri, xforms, and Specter. Potpuri is an old-fashioned “missing parts of clojure.core” library and xforms is the same for transducers. Specter is the closest thing to lenses for Clojure. It’s powerful but takes some time to learn. I recommend trying it out for some simple tasks – soon you’ll see opportunities for it everywhere.

What about …?

I haven’t included any SQL libraries, HTTP clients or servers, or async libraries, because I don’t have a clear recommendation for any of these (important!) categories.


Why interceptors?

At Metosin, we have been thinking about interceptors in Clojure and ClojureScript. We’ve thought about them so much that in fact we made our own interceptor library for Clojure, called Sieppari1. To understand why, let’s take a look at the pros and cons of using interceptors instead of middleware.

Interceptors are a Clojure pattern, pioneered by the Pedestal framework, that replaces the middleware pattern used by Ring. In Pedestal, they are used for handling HTTP requests, but they can be used for handling all kinds of requests. For example in re-frame they’re used in handling web frontend events such as button clicks.

At Metosin, we’ve used them in a bunch of projects and we’re developing Sieppari to be used with reitit, our (latest) HTTP routing library.

Let’s work this out

In Ring, a HTTP request handler is a function that takes a request map and returns a response map. Something like this:

(defn my-handler [request]
  {:status 200,
   :headers {"Content-Type" "text/plain"},
   :body "hello!!"}))

To enhance the behavior of the handler in reusable way, you can wrap it with a higher-order function that takes the handler as parameter. This can be used to implement features like content encoding and decoding and authentication.

For example, here’s a debugging middleware that prints the incoming request map and the outgoing response map:

(defn print-middleware [handler]
  (fn [request]
    (prn :REQUEST request)
    (let [response (handler request)]
      (prn :RESPONSE response))))

The good thing about middleware is that they’re simple to implement: it’s just a Clojure function and you can use standard constructs such as try-catch. They’re fast, too.

The problems start when you try handle asynchronous operations. Ring specifies async handlers as functions that take callbacks for sending a response and raising an exception as arguments. We have to write a separate version of our debugging middleware for asynchronous handlers.

(defn debug-middleware [handler]
  (fn [request respond raise]
    (prn :REQUEST request)
    (handler request
             (fn [response]
               (prn :RESPONSE response)
               (respond response))
             raise)))

Using the same code for the synchronous and asynchronous handler is tricky and error handling gets difficult.

Interceptors offer a solution: you split the middleware in two phases2, :enter and :leave (or :before and :after as they’re called by re-frame). :enter is called before executing the handler, :leave is called afterwards. Both phases get a context map as a parameter and they return an updated context map. The request is under the key :request and the handler’s response is put under :response.

(def debug-interceptor
  {:enter
   (fn [{:keys [request] :as context}]
     (prn :REQUEST request)
     context)
   :leave
   (fn [{:keys [response] :as context}]
     (prn :RESPONSE response)
     context)})

Middleware can be composed by nesting function calls. With interceptors that does not work, so you need to have an executor that takes a chain of interceptors (called a queue) and executes them in order.

A cool thing you can now do is that if your interceptor returns an asynchronous result (a deferred or core.async channel for example), the executor can wait for it, and if the interceptor returns a synchronous result, the executor can act on it directly. This allows you to use the same interceptors for synchronous and asynchronous operations. The downside is that the executor is bound to be slower than nested function calls.

Another downside is that structures like try-catch and with-open do not work anymore. To allow proper error handling, interceptors have an optional :error phase that gets called if any of the inner interceptors throws an exception.

The queue as data

Middleware do not have to call the handler. For example, an authorization middleware may decide that a request is not authorized and instead of calling the handler, it returns an error response.

Interceptors go further: the remaining queue and the stack of the already-entered interceptors are exposed in the context map and you can manipulate them. If your authorization middleware wants to return early, you can (assoc context :queue []). Another example is that you can have a routing interceptor that pushes route-specific interceptors and a handler to the queue.

Finally, since your interceptor chain is now data instead of middleware-nesting code, you can do fancy tricks like dynamically re-order interceptors based on the dependencies between them. angel-interceptor is an implementation of this and Sieppari supports it as well. I’m a bit skeptical about whether there are real use cases for this, but it’s there if you need it.

Summary


  1. It’s not yet ready for production. At the time of writing, the latest release is 0.0.0-alpha5.
  2. You can do this without interceptors, of course. See e.g. how the session middleware is implemented in the Ring codebase.


For more posts, see archive.