Static-Free ClojureScript REPL

April 14, 2016

The general advice is to leave :static-fns turned off for development with the ClojureScript REPL.




If you are curious and turn it on, most things will work anyway. This post gives a concrete example illustrating what can go wrong if you do turn it on.

Let's say you have a multi-arity function f

(defn f 
  ([a] 
    (prn a)) 
  ([a b] 
    (prn a b)))

and you define another function g that calls it:

(defn g 
  [] 
  (f 1 2))

If you try (g) at the REPL, it will print 1 2.

Now, let's say you revise f to be variadic:

(defn f 
  ([a] 
    (prn a)) 
  ([a & [b]] 
    (prn a b)))

With this change, (g) will still print 1 2 as expected.

But, let's try this with :static-fns set to true. Try it with your favorite ClojureScript REPL.

If you want to try this with Planck, you can simply re-launch it passing -s or --static-fns as described in Planck Static Function Dispatch.

If you go through the same sequence, you will get an error indicating the following when you evaluate (g) the second time:

cljs.user.f.cljs$core$IFn$_invoke$arity$2 is not a function.

This is the static dispatch to the 2-arity version of f.

If you reverse the order of the definition of f and g, you will then get the opposite error:

cljs.user.f.cljs$core$IFn$_invoke$arity$variadic is not a function.

You can see these dispatch mechanisms statically embedded in the definition of g if you look at its emitted JavaScript.

On the other hand, if you have :static-fns disabled, then the code emitted for g will involve

cljs.user.f.call(null,(1),(2))

which works under the re-definition of f.

While this dispatch mechanism is slower, it affords you the dynamic-dispatch Lispy flexibility you take for granted while redefining things at the REPL.

Tags: ClojureScript