Def Vars in ClojureScript REPLs

June 9, 2015

Fairly recently, ClojureScript gained static vars. A good intro, right from the horse's mouth is: What's in a Var?

But there remains a significant difference between Clojure and ClojureScript when it comes to def and derived forms: In Clojure, def returns the var being defined, while in ClojureScript it returns the value of the var.




Here is an example from the Clojure REPL:

user=> (def a 3)
#'user/a

And here is the same in the ClojureScript REPL:

cljs.user=> (def a 3)
3

No biggie. But, consider function definitions.

Clojure:

user=> (defn square [x] (* x x))
#'user/square

ClojureScript:

cljs.user=> (defn square [x] (* x x))
#<function cljs$user$square(x){
return (x * x);
}> 

Yeah. This is not so bad, and seeing the emitted JavaScript can arguably be useful at times. But for larger function definitions, this can get to be a bit distracting and annoying, IMHO.

But now that we have vars, we can revise this behavior, at least for defs evaluated at the REPL, leaving compiled code to the existing behavior.

Here is some sample output for work being done towards CLJS-934:

cljs.user=> (def a 3)
#'cljs.user/a
cljs.user=> (defn square [x] (* x x))
#'cljs.user/square

Yay! Civilized even!

Another nice consequence is that you can define infinite lazy sequences in the REPL:

cljs.user=> (def naturals (map inc (range)))
#'cljs.user/naturals
cljs.user=> (take 11 naturals)
(1 2 3 4 5 6 7 8 9 10 11)

You can also (but only in the REPL), use the return value of a def form as the var it really is:

cljs.user=> @(def a 3)
3
cljs.user=> ((defn square [x] (* x x)) 3)
9

Note this last example exercises the evaluation rule that, for function position, a var evaluates to its value (the square function itself), which is then applied.

If you want the old behavior—perhaps to see the emitted JavaScript—it is just a @ away:

cljs.user=> @(defn square [x] (* x x))
#<function cljs$user$square(x) {
return (x * x);
}>
Tags: ClojureScript