A Pattern for Removing Impure Reads from Otherwise Pure Functions

Sometimes I find I've got a function like this:

(defn calculate-something [a]
  (if (some-test? a)
    (let [b (read-from-db a)]
      (some-transform a b))
    (some-other-transform a)))

Basically, I need to read from the database (or any other impure place) in the middle of the function, after checking whether the read is necessary/possible.

That function has a signature like [& params] => result. A quick way to make that function pure is to give it a signature of [memo & params] => effect, where effect is either [:result some-data] or [:query & query-params].

So we'd write the above as:

(defn calculate-something [memo a]
  (let [q [:read-from-db a]
        a? (some-test? a)]
    (cond
      (and a? (contains? memo q))
      (let [b (get memo q)]
        [:result (some-transform a b)])

      a? [:query q]

      :else [:result (some-other-transform a)])))

Then in another layer of the app where purity isn't required, call the function in a loop until it returns a result, and interpret :query forms as needed.

(defn get-something! [conn a]
  (loop [memo nil i 0]
    (when (> i 10) (throw (ex-info "Too many queries")))
    (let [[cmd data] (calculate-something memo a)]
      (case cmd
        :result data
        :query  (let [memo (assoc memo data (execute-query! conn data))]
                  (recur memo (inc i))))))

You could make this support getting multiple queries to execute per call of the calculate-something function.

execute-query! could be a multimethod or a simple case expression.

You could also make the impure wrapper agnostic of the pure function it calls:

(defn run-with-queries [conn f & params]
  (loop [memo nil i 0]
    (when (> i 10) (throw (ex-info "Too many queries")))
    (let [[cmd data] (apply f memo params)]
      (case cmd
        :result data
        :query  (let [memo (assoc memo data (execute-query! conn data))]
                  (recur memo (inc i)))))))

Published: 2026-06-29

Tagged: clojure

Clojure Optimization Tools and Techniques

I intend to add tips about optimizing Clojure programs here as I learn them.

Heap Analysis

Your Clojure program is using a lot of memory and you don't know why. Take a heap dump, load it in Eclipse Memory Analyze (MAT), look at the "Leak Suspects", and start with the biggest.

Published: 2026-05-15

Tagged: clojure

Lists in Web Components

Suppose you have some UI in a website that should render a list of items. Let's call it FancyList. Usage locations of FancyList provide the list of items, each with a label, and, crucially, any other content that is rendered however the usage location requires.

In UI libraries like React and Svelte, components allow customized rendering using callback functions or slots. In React this might look like:

See the Pen Untitled by tom connors (@tomconnors) on CodePen.

In Svelte we use snippets but the concept is pretty much the same; the caller defines how items are rendered and the FancyList calls that logic.

We don't have a similarly convenient option for rendering child element lists when using web components. You can't pass a function to a web component with just HTML:

<!-- This doesn't solve our problem -->
<fancy-list items="[...]"></fancy-list>

The best option I've found is conceptually pretty simple: instead of passing the items as an attribute of the web component, pass them as children. Structure the children so that the web component can easily pull out the information it needs, and include any customized rendering directly in the DOM.

See the Pen Untitled by tom connors (@tomconnors) on CodePen.

There are some important things to note:

  • We read the DOM the caller provided in parseLightDom. You can imagine more complex versions of this function for more complex situations.
  • We use a mutation observer to keep track of the children provided by the caller so we can rerender if the items change.
  • Instead of directly putting the elements provided by the caller into the WC's shadow DOM, we clone them. This works in more situations than just using innerHTML.
  • We don't use <slot> elements because we have a list of items where each can render differently.
  • This approach requires more HTML. Your HTML templating should make that manageable and compression should negate any performance concerns.

Published: 2026-02-28

Tagged: clojure

Archive