Evaluation

Special Forms

The following ClojureScript special forms are identical to their Clojure
cousins: if, do, let, letfn, quote, loop, recur, throw, and try.

var notes

Vars are not reified at runtime. When the compiler encounters the var special form it emits a Var instance reflecting compile time metadata. (This satisfies many common static use cases.)

def notes

def produces ordinary JS variables

:private metadata is not enforced by the compiler

:const metadata in ClojureScript disallows re-def and supports case. In Clojure :const allows re-def but value inlined at use site.

A def form evaluates to the value of the init form (instead of the var), unless the :def-emits-var compiler option is set (which defaults to true for REPLs)

if notes

the section about Java’s boolean boxes is irrelevant in ClojureScript

fn notes

There is currently no runtime enforcement of arity when calling a fn

monitor-enter, monitor-exit, and locking are not implemented

Macros

ClojureScript’s macros must be defined in a different compilation stage than the one from
where they are consumed. One way to achieve this is to define them in one namespace and use them from another.

Macros are referenced via the :require-macros keyword in
namespace declarations:

(ns my.namespace
(:require-macros [my.macros :as my]))

Sugared and other ns variants can be employed in lieu of using the :require-macros primitive; see Namespaces below for details.

Macros are written in *.clj or *.cljc files and are compiled either as Clojure when
using regular ClojureScript or as ClojureScript when using bootstrapped / self-host
ClojureScript. One point of note is that the code generated by Clojure-based
ClojureScript macros must target the capabilities in ClojureScript.

ClojureScript namespaces can require macros from the selfsame namespace, so long as they
are kept in different compilation stages. So, for example a foo.cljs or foo.cljc file can make
use of a foo.cljc or foo.clj file for its macros.

Unlike in Clojure, in ClojureScript a macro and a function can have the same name (for example the cljs.core/+ macro and cljs.core/+ function can coexist).

You may be wondering: “If that’s the case, which one do I get?” ClojureScript (unlike Clojure) has two distinct stages that make use of two separate non-interacting namespaces. Macroexpansion occurs first, so a form like (+ 1 1) initially involves the cljs.core/+ macro. On the other hand, in a form like (reduce + [1 1]), the + symbol is not in operator position, and passes untouched through macroexpansion to analysis/compilation where it is resolved as the cljs.core/+ function.

Other Functions

In JVM ClojureScript it is not possible to dynamically set *assert* to false at runtime. Instead the :elide-asserts compiler option must be used to effect elision. (On the other hand, in self-hosted ClojureScript *assert* behaves identically to Clojure.)

Data Structures

Numbers

Currently ClojureScript numbers are just JavaScript numbers

Coercions are not implemented, since there are currently no types to coerce to

Characters

JavaScript has no character type. Clojure characters are represented internally as single-character strings

Keywords

ClojureScript keywords are not guaranteed to be identical?, for fast equality testing use keyword-identical?

Collections

Persistent collections available

Ports of Clojure’s implementations

Transient support in place for persistent vectors, hash maps and hash sets

Most but not all collection fns are implemented

Seqs

Seqs have the same semantics as in Clojure, and almost all Seq library functions are available in ClojureScript.

Protocols

defprotocol and deftype, extend-type, extend-protocol work as in Clojure

Protocols are not reified as in Clojure, there are no runtime protocol objects

Metadata

Works as in Clojure.

Namespaces

Namespaces in ClojureScript are compiled to Google Closure namespaces which are represented as nested JavaScript objects. Importantly this means that namespaces and vars have the potential to clash - however the compiler can detect these problematic cases and will emit a warning when this occurs.

You must currently use the ns form only with the following caveats

You must use the :only form of :use

:require supports :as, :refer, and :rename

all options can be skipped

in this case a symbol can be used as a libspec directly

that is, (:require lib.foo) and (:require [lib.foo]) are both supported and mean the same thing

:rename specifies a map from referred var names to different symbols (and can be used to prevent clashes)

ClojureScript types and records should be brought in with :use or :require :refer, not :import ed

Macros must be defined in a different compilation stage than the one from
where they are consumed. One way to achieve this is to define them in one namespace and use them from another. They are referenced via the :require-macros / :use-macros options to ns

:require-macros and :use-macros support the same forms that :require and :use do

Implicit macro loading: If a namespace is required or used, and that namespace itself requires or uses macros from its own namespace, then the macros will be implicitly required or used using the same specifications. Furthermore, in this case, macro vars may be included in a :refer or :only spec. This oftentimes leads to simplified library usage, such that the consuming namespace need not be concerned about explicitly distinguishing between whether certain vars are functions or macros. For example:

will result in test/is resolving properly, along with the test-var function and the deftest macro being available unqualified.

Inline macro specification: As a convenience, :require can be given either :include-macros true or :refer-macros
[syms…​]. Both desugar into forms which explicitly load the matching Clojure file containing macros. (This works independently of whether the namespace being required internally requires or uses its own macros.) For example:

Auto-aliasing clojure namespaces: If a non-existing clojure.* namespace is required or used and a matching cljs.* namespace exists, the cljs.* namespace will be loaded and an alias will be automatically established from the clojure.* namespace to the cljs.* namespace. For example:

(ns testme.core (:require [clojure.test]))

will be automatically converted to

(ns testme.core (:require [cljs.test :as clojure.test]))

Libs

Existing Clojure libs will have to conform to the ClojureScript subset in order to work in ClojureScript.

Additionally, macros in Clojure libs must be compilable as ClojureScript in order to be consumable in
self-host / bootstrapped ClojureScript via its cljs.js/*load-fn* capability.

In ClojureScript Foo/bar always means that Foo is a namespace. It cannot be used for the Java static field access pattern common in Clojure as there’s no reflection information in JavaScript to determine this.

The special namespace js provides access to global properties:

js/Infinity
=> Infinity

To access object properties (including functions that you want as a value, rather than to execute) use a leading hyphen:

(.-NEGATIVE_INFINITY js/Number)
=> -Infinity

Hinting

While ^long and ^double—when used on function parameters—are type declarations in Clojure, they are type hints in ClojureScript.

Type hinting is primarily used to avoid reflection in Clojure. In ClojureScript, the only type hint of significance is the ^boolean type hint: It is used to avoid checked if evaluation (which copes with the fact that, for example, 0 and "" are false in JavaScript and true in ClojureScript).

Compilation and Class Generation

Compilation is different from Clojure:

All ClojureScript programs are compiled into (optionally optimized) JavaScript.

Individual files can be compiled into individual JS files for analysis of output