ClojureScript clojure Namespace Aliasing
Update March 21, 2018: Content from this post has been incorporated into the ClojureScript site Guide: ns Forms.
Some namespaces—like clojure.string
and clojure.set
—are available for use in ClojureScript, even though the first segment in those namespaces is clojure
. But then others—like cljs.pprint
, cljs.test
, and now cljs.spec
—live under cljs
.
Why the difference? Ideally, there'd be none. But, if you look at, say, the port of clojure.pprint
for use with ClojureScript, it involves a macro namespace. This is where the problem lies. Since the JVM ClojureScript compiler uses Clojure for execution, there would be a namespace collision if the port were not moved to cljs.pprint
. In short, the clojure.pprint
namespace was taken.
A consequence of this is that we have to remember to use cljs.*
for some namespaces when writing ClojureScript. And, if you are writing portable code, you need to employ reader conditionals. See for example the dance done with clojure.test
and cljs.test
in Clojure Reader Conditionals by Example.
Over the years, several affordances have been added to the ClojureScript ns
special, all in the name of simplifying usage with respect to macros, and, in many cases, making ClojureScript ns
forms look closer to the Clojure equivalent.
Now, there is a new simplification that will be coming to a ClojureScript compiler near you: You can use clojure
as an alias for cljs
in the first segment of namespaces in the case of nonexistent clojure.*
namespaces that can be mapped to cljs.*
namespaces.
A simple example:
(ns foo.core
(:require [clojure.test]))
can be used instead of
(ns foo.core
(:require [cljs.test]))
If you do this, the ClojureScript compiler will first see if it can load the clojure.test
namespace. Since it doesn't exist, it will fall back to loading cljs.test
.
At the same time, an alias is set up from clojure.test
to cljs.test
, as if you had written:
(ns foo.core
(:require [cljs.test :as clojure.test]))
This is important because it allows you to have code that qualifies symbols, as in clojure.test/test-var
.
With this change, along with a recent change to infer macro vars in :refer
specs (see “Implicit Refer” in ClojureScript Macro Sugar), the following code works just fine in ClojureScript:
(ns foo.core-test
(:require [clojure.test :as test :refer [deftest is]]))
(deftest foo-test
(is (= 3 4)))
(test/test-var #'foo-test)
And, more importantly: This is the exact same code you'd write in Clojure. No reader conditionals needed!
This stuff also works in the require
ClojureScript REPL special. So for example, you can do:
(require '[clojure.spec :as s])
Then (s/def ::even? (s/and number? even?))
will work just fine. The reason for this is that the require
REPL special is implemented in terms of the ns
special.
¡Bueno! This should go a long way towards easing the differences between ClojureScript and Clojure ns
forms!
Update Aug. 12, 2016: These features are in the ClojureScript 1.9.198 release.