Learn ClojureScript in Y Minutes

ClojureScript is a dialect of the Clojure language developed for JavaScript environments. It has most of the functionality of Clojure with small adaptations owing to the differences in the underlying platform.

; Comments start with semicolons.; ClojureScript is written in "forms", which are just; lists of things inside parentheses, separated by whitespace.;; The clojure reader assumes that the first thing is a; function or macro to call, and the rest are arguments.; The first call in a file should be ns, to set the namespace(nslearn.core); More basic examples:; str will create a string out of all its arguments(str"Hello"" ""World"); => "Hello World"; Math is straightforward(+11); => 2(-21); => 1(*22); => 4(/12); => 0.5 (in Clojure, this would return `1/2`); Equality is =(=11); => true(=21); => false; You need not for logic, too(nottrue); => false; Nesting forms works as you expect(+1(-32)); = 1 + (3 - 2) => 2; Types;;;;;;;;;;;;;; ClojureScript uses JavaScript's types for booleans, strings and numbers.; Use `type` to inspect them.(type1); => #object[Number "function Number() { [native code] }"](type1.); Same as above, no distinction between int and double(type""); Strings always double-quoted(typefalse); => #object[Boolean "function Boolean() { [native code] }"](typenil); The "null" value is called nil; If you want to create a literal list of data, use ' to stop it from; being evaluated'(+12); => (+ 1 2); (shorthand for (quote (+ 1 2))); Collections & Sequences;;;;;;;;;;;;;;;;;;;; Lists are linked-list data structures, while Vectors are array-backed.(type[123]); => cljs.core/PersistentVector(type'(123)); => cljs.core/List; A list would be written as just (1 2 3), but we have to quote; it to stop the reader thinking it's a function.; Also, (list 1 2 3) is the same as '(1 2 3); "Collections" are just groups of data; Both lists and vectors are collections:(coll?'(123)); => true(coll?[123]); => true; "Sequences" (seqs) are abstract descriptions of lists of data.; Only lists are seqs.(seq?'(123)); => true(seq?[123]); => false; A seq need only provide an entry when it is accessed.; So, seqs which can be lazy -- they can define infinite series:(range4); => (0 1 2 3)(range); => (0 1 2 3 4 ...) (an infinite series, don't evaluate!)(take4(range)); (0 1 2 3); Use cons to add an item to the beginning of a list or vector(cons4[123]); => (4 1 2 3)(cons4'(123)); => (4 1 2 3); Conj will add an item to a collection in the most efficient way.; For lists, they insert at the beginning. For vectors, they insert at the end.(conj[123]4); => [1 2 3 4](conj'(123)4); => (4 1 2 3); Use concat to add lists or vectors together(concat[12]'(34)); => (1 2 3 4); Use filter, map to interact with collections(mapinc[123]); => (2 3 4)(filtereven?[123]); => (2); Use reduce to reduce them(reduce+[1234]); = (+ (+ (+ 1 2) 3) 4); => 10; Reduce can take an initial-value argument too(reduceconj[]'(321)); = (conj (conj (conj [] 3) 2) 1); => [3 2 1]; Functions;;;;;;;;;;;;;;;;;;;;;; Use fn to create new functions. A function always returns; its last statement.(fn[]"Hello World"); => fn; (You need extra parens to call it)((fn[]"Hello World")); => "Hello World"; You can create a var using def(defx1)x; => 1; Assign a function to a var(defhello-world(fn[]"Hello World"))(hello-world); => "Hello World"; You can shorten this process by using defn(defnhello-world[]"Hello World"); The [] is the list of arguments for the function.(defnhello[name](str"Hello "name))(hello"Steve"); => "Hello Steve"; You can also use this shorthand to create functions:(defhello2#(str"Hello "%1))(hello2"Fanny"); => "Hello Fanny"; You can have multi-variadic functions, too(defnhello3([]"Hello World")([name](str"Hello "name)))(hello3"Jake"); => "Hello Jake"(hello3); => "Hello World"; Functions can pack extra arguments up in a seq for you(defncount-args[&args](str"You passed "(countargs)" args: "args))(count-args123); => "You passed 3 args: (1 2 3)"; You can mix regular and packed arguments(defnhello-count[name&args](str"Hello "name", you passed "(countargs)" extra args"))(hello-count"Finn"123); => "Hello Finn, you passed 3 extra args"; Maps;;;;;;;;;;; Hash maps and array maps share an interface. Hash maps have faster lookups; but don't retain key order.(type{:a1:b2:c3}); => cljs.core/PersistentArrayMap(type(hash-map:a1:b2:c3)); => cljs.core/PersistentHashMap; Arraymaps will automatically become hashmaps through most operations; if they get big enough, so you don't need to worry.; Maps can use any hashable type as a key, but usually keywords are best; Keywords are like strings with some efficiency bonuses(type:a); => cljs.core/Keyword(defstringmap{"a"1,"b"2,"c"3})stringmap; => {"a" 1, "b" 2, "c" 3}(defkeymap{:a1,:b2,:c3})keymap; => {:a 1, :c 3, :b 2}; By the way, commas are always treated as whitespace and do nothing.; Retrieve a value from a map by calling it as a function(stringmap"a"); => 1(keymap:a); => 1; Keywords can be used to retrieve their value from a map, too!(:bkeymap); => 2; Don't try this with strings.;("a" stringmap); => #object[TypeError TypeError: "a".call is not a function]; Retrieving a non-present key returns nil(stringmap"d"); => nil; Use assoc to add new keys to hash-maps(defnewkeymap(assockeymap:d4))newkeymap; => {:a 1, :b 2, :c 3, :d 4}; But remember, clojure types are immutable!keymap; => {:a 1, :b 2, :c 3}; Use dissoc to remove keys(dissockeymap:a:b); => {:c 3}; Sets;;;;;;(type#{123}); => cljs.core/PersistentHashSet(set[123123321321]); => #{1 2 3}; Add a member with conj(conj#{123}4); => #{1 2 3 4}; Remove one with disj(disj#{123}1); => #{2 3}; Test for existence by using the set as a function:(#{123}1); => 1(#{123}4); => nil; There are more functions in the clojure.sets namespace.; Useful forms;;;;;;;;;;;;;;;;;; Logic constructs in clojure are just macros, and look like; everything else(iffalse"a""b"); => "b"(iffalse"a"); => nil; Use let to create temporary bindings(let[a1b2](>ab)); => false; Group statements together with do(do(print"Hello")"World"); => "World" (prints "Hello"); Functions have an implicit do(defnprint-and-say-hello[name](print"Saying hello to "name)(str"Hello "name))(print-and-say-hello"Jeff");=> "Hello Jeff" (prints "Saying hello to Jeff"); So does let(let[name"Urkel"](print"Saying hello to "name)(str"Hello "name)); => "Hello Urkel" (prints "Saying hello to Urkel"); Use the threading macros (-> and ->>) to express transformations of; data more clearly.; The "Thread-first" macro (->) inserts into each form the result of; the previous, as the first argument (second item)(->{:a1:b2}(assoc:c3);=> (assoc {:a 1 :b 2} :c 3)(dissoc:b));=> (dissoc (assoc {:a 1 :b 2} :c 3) :b); This expression could be written as:; (dissoc (assoc {:a 1 :b 2} :c 3) :b); and evaluates to {:a 1 :c 3}; The double arrow does the same thing, but inserts the result of; each line at the *end* of the form. This is useful for collection; operations in particular:(->>(range10)(mapinc);=> (map inc (range 10)(filterodd?);=> (filter odd? (map inc (range 10))(into[]));=> (into [] (filter odd? (map inc (range 10))); Result: [1 3 5 7 9]; Modules;;;;;;;;;;;;;;;; Use require to import a module(require'clojure.string); Use / to call functions from a module; Here, the module is clojure.string and the function is blank?(clojure.string/blank?""); => true; You can give a module a shorter name on import(require'[clojure.string:asstr])(str/replace"This is a test."#"[a-o]"str/upper-case); => "THIs Is A tEst."; (#"" denotes a regular expression literal); You can choose a subset of functions to import, too(require'[clojure.set:refer[intersection]]); Now you can use `intersection` directly(intersection#{123}#{234}); => #{2 3}; You can use require from a namespace using :require.; You don't need to quote your modules if you do it this way.(nstest.core(:require[clojure.string:asstr][clojure.set:asset])); JavaScript;;;;;;;;;;;;;;;;;; JavaScript has a huge and ecosystem, so; you'll want to learn how to get at it.; Prefix with `js/` and use the class name with a `.` at the end; to make a new instance. Or use `new`(js/Date.); JS: new Date()(newjs/Date); JS: new Date(); Use `.` to call methods.; Or, use the `.method` shortcut(.(js/Date.)getTime); <a timestamp>(.getTime(js/Date.)); exactly the same thing.; You can access properties via `.` as well.; Or use the `.-property` shortcut(.js/Math-PI); JS: Math.PI(.-PIjs/Math); JS: Math.PI; You can access properties and call functions in nested objects via `..`(..js/self-Math-PI); JS: self.Math.PI(..js/self-Math(pow102)); JS: self.Math.pow(10, 2); For methods you can use the `.method` shortcut, again(.Math.powjs/self102); JS: self.Math.pow(10, 2); You can tag a clojure map literal with `#js` to turn it into json; Don't forget the inner `#js` tag!#js{:a1:n#js{:b2:c3}}; JS: {"a": 1, "n": {"b": 2, "c": 3}}; For symbolic references use `clj->js`(defdat{:a1:n{:b2:c3}})(clj->jsdat); JS: {"a": 1, "n": {"b": 2, "c": 3}}; To turn json into clojure data structures, use `js->clj`(defjsdat(clj->jsdat))(js->cljjsdat); => {"a" 1, "n" {"b" 2, "c" 3}}(js->cljjsdat:keywordize-keystrue); => {:a 1, :n {:b 2, :c 3}}; You can set properties on a js object via `set!`; Note that cljs functions are also js functions(set!(.-onclickjs/document)(fn[e](.logjs/consolee))); JS: document.onclick = function(e) { console.log(e) }(set!(..js/self-document-onclick)#(.logjs/console%)); JS: self.document.onclick = function(e) { console.log(e) }; STM;;;;;;;;;;;;;;;;;; Software Transactional Memory is the mechanism clojure uses to handle; persistent state. There are a few constructs in clojure that use this.; An atom is the simplest. Pass it an initial value(defmy-atom(atom{})); Update an atom with swap!.; swap! takes a function and calls it with the current value of the atom; as the first argument, and any trailing arguments as the second(swap!my-atomassoc:a1); Sets my-atom to the result of (assoc {} :a 1)(swap!my-atomassoc:b2); Sets my-atom to the result of (assoc {:a 1} :b 2); Use '@' to dereference the atom and get the valuemy-atom;=> Atom<#...> (Returns the Atom object)@my-atom; => {:a 1 :b 2}; Here's a simple counter using an atom(defcounter(atom0))(defninc-counter[](swap!counterinc))(inc-counter)(inc-counter)(inc-counter)(inc-counter)(inc-counter)@counter; => 5; Other STM constructs are refs and agents.; Refs: http://clojure.org/refs; Agents: http://clojure.org/agents

Further Reading

This is far from exhaustive, but hopefully it’s enough to get you on your feet.