Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Differences from Clojure

Mike Fikes edited this page · 68 revisions

What follows is a section-by-section review of the sections on the left-hand navigation panel of, enumerating what is different in ClojureScript and in some cases the similarities.


The rationale for ClojureScript is much the same as for Clojure, with JavaScript in the role of platform, and additional emphasis on the reach of JS, as it is obviously not as rich a platform.

A deeper discussion on ClojureScript's rationale can be found elsewhere on this site.

State and Identity

Same as Clojure. Clojure's identity model is simpler and more robust than mutable state, even in single threaded environments.

Dynamic Development

ClojureScript's REPL can be launched from within the Clojure REPL. See Quick Start for details.

Functional Programming

ClojureScript has the same immutable persistent collections as Clojure on the JVM.


ClojureScript's macros are written in Clojure, and are referenced via the require-macros keyword in namespace declarations:

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

The :as prefix selector is required in :require-macros. One point of note is that the code generated by ClojureScript macros must target the capabilities in ClojureScript.

Runtime Polymorphism

  • ClojureScript protocols have the same semantics as Clojure protocols.

Concurrent Programming

Clojure's model of values, state, identity, and time is valuable even in single-threaded environments.

  • Atoms work as in Clojure
  • No Refs nor STM
  • The user experience of binding is similar to that in Clojure
    • Vars
      • not reified at runtime
      • many development time uses of reification are obviated by access to Clojure data structures via the analyzer
    • def produces ordinary JS variables
  • Agents are currently not implemented

Hosted on the JVM

Getting Started

See Quick Start

The Reader

  • Numbers
    • ClojureScript currently only supports integer and floating point literals that map to JavaScript primitives
      • Ratio, BigDecimal, and BigInteger literals are currently not supported
  • Characters
    • ClojureScript does not have character literals, instead characters are the same as in JavaScript (i.e. single-character strings)
  • Lists, Vectors, Maps, and Set literals are the same as in Clojure
  • Macro characters
    • Because there is no character type in ClojureScript, \ produces a single-character string.
  • read
    • The read and read-string functions are located in the cljs.reader namespace

The REPL and main

  • See Quick Start for instructions the ClojureScript REPL.
  • main support is currently not implemented


  • ClojureScript has the same evaluation rules as Clojure
  • load and load-file are planned
  • Runtime eval is not supported in ClojureScript

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)
  • 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 are written in Clojure. (See the Lisp differences section above).

Other Functions

  • printing
    • *out* is currently not implemented
  • regex support
  • asserts
    • It is currently not possible to dynamically set *assert* to false at runtime. Instead the :elide-asserts compiler option must be used to effect elision.

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 have the same semantics as in Clojure, and almost all Seq library functions are available in ClojureScript.


  • defprotocol and deftype, extend-type, extend-protocol work as in Clojure
  • Protocols are not reified as in Clojure, there are no runtime protocol objects
  • Some reflective capabilities (satisifies?) work as in Clojure
    • satisfies? is a macro and must be passed a protocol name
  • extend is not currently implemented
  • specify, extend immutable values to protocols - instance level extend-type without wrappers


Works as in Clojure.


  • You must currently use the ns form only with the following caveats
    • You must use the :only form of :use
    • :require supports :as and :refer
      • both options can be skipped
      • in this case a symbol can be used as a libspec directly
        • that is, (:require and (:require []) are both supported and mean the same thing
      • prefix lists are not supported
    • The only option for :refer-clojure is :exclude
    • :import is available for importing Google Closure classes
      • ClojureScript types and records should be brought in with :use or :require :refer, not :imported
  • Macros are written in Clojure, and are referenced via the new :require-macros / :use-macros options to ns
    • :require-macros and :use-macros support the same forms that :require and :use do


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

Vars and the Global Environment

  • def and binding work as in Clojure
    • but on ordinary js variables
  • Atoms work as in Clojure
  • Refs and Agents are not currently implemented
  • Validators work as in Clojure
  • intern not implemented - no reified Vars

Refs and Transactions

Refs and transactions are not currently supported.


Agents are not currently supported.


Atoms work as in Clojure.

Host Interop

The host language interop features (new, /, ., etc.) work as in Clojure where possible, e.g.:

=> "en"

(let [sb (goog.string.StringBuffer. "hello, ")]
 (.append sb "world")
 (.toString sb))
=> "hello, world"

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

(.-Infinity js/window)
 => Infinity

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
  • Production compilation is whole-program compilation via Google Closure compiler
  • gen-class, gen-interface, etc. are unnecessary and unimplemented in ClojureScript

Other Libraries

ClojureScript currently includes the following non-core namespaces ported from Clojure:

  • clojure.set
  • clojure.string
  • clojure.walk
  • clojure.core.reducers
    • fold is currently an alias for reduce

Differences with other Lisps

ClojureScript is the same (at a Lisp level) as Clojure except for the following:

  • no runtime evaluation
  • no runtime compilation


Clojure and ClojureScript share the same Contributor Agreement and development process.

Something went wrong with that request. Please try again.