Consider run!

January 16, 2016

Sometimes you need to perform a side effect for each element in a sequence. For the sake of argument, let's say we want to prn each of the items in [0 1 2 3 4].




The existing core function doseq can be used to accomplish this:

(doseq [x [0 1 2 3 4]]
  (prn x))

Since doseq is modeled after for, lots of sophisticated things can be done using its mini-language, with :let, :while, and :when.

But, for applying prn to each of the elements, you may be tempted to reach for this simpler looking map form:

(map prn [0 1 2 3 4])

While this has appeal, it is laced with subtle issues surrounding evaluation and lazy sequences.

When you have something like this, you don't need to resort to dorun to force evaluation. Instead, simply replace map with run!, directly indicating your intent:

(run! prn [0 1 2 3 4])

As its docstring indicates, run! is based on reduce, which is eager, thus side-stepping any issues with lazy sequences. Also with run!, no temporary sequence is produced and discarded as would be with (dorun (map ...)).

And, since it is based on reduce, this gives you a way to terminate processing early—you can use reduced:

(run! (fn [n]
        (if (< n 3)
          (prn n)
          (reduced nil)))
  (range))

This will apply prn to only the first 3 elements of the infinite sequence returned by (range) and terminate:

0
1
2

Note that, while the intent of run! is that it be used solely for side effects (and thus is without a meaningful return value), it can return non-nil in its initially-released form in Clojure and ClojureScript.

The run! function was introduced with the work done to add transducers to Clojure 1.7, also first appearing in ClojureScript 0.0-2301.

Tags: Clojure ClojureScript