Created by Pavel Klavík
ClojureScript is the language version which compiles to JavaScript. It uses Google Closure Compiler which optimizes and produces minimal code.
The original Clojure implementation adressed both JVM and CLR (.NET). Today JVM is most used but CLR should be still functional. (I personally have never used it.)
The original most used version of Clojure runs on JVM. It integrates well with existing Java libraries and on the other hand can be used to create new libraries used directly from Java. It is enought to add clojure.jar into the project.
There exist Clojure versions for other platforms.
The language is hosted on other platforms and we have a direct access to the environment. Therefore, it is straightforward to use an arbitrary library from the environment and on the other hand the native code can use a code written in Clojure. It is easy to add Clojure into an existing project. In many companies, it was sneaked in by some programmer and was later adapted by entire teams.
function performMagic(value, list) {
return list.concat(value)
.map(v => v + 1)
.filter(v => v % 2 === 0)
.reduce((accumulator, v) => v + accumulator);
}(defn perform-magic [value list]
(->> list
(cons value)
(map inc)
(filter odd?)
(reduce +)))
Instead f(x,y,z) write (f x y z).
Clojure has dynamic types based on the environment. There are many myths surrounding static typing:
Static types are mostly addressing typos (i.e., swapping function arguments). They do not reveal serious problems. The cost is lesser generality and a lot of additional code.
One can use automatic tests, REPL and spec in Clojure.
Used by companies such as Apple, Facebook, Netflix, WalmartLabs, ...
An illustration on changes in the code of the game Flappy Bird. The state of the app is preserved throughout the code changes. This completely changes the way you program.
Consider the following Java code:
JFrame f = new JFrame();
JLabel l = new JLabel("Hello World");
l.setHorizontalAlignment(SwingConstants.CENTER);
f.add(l);
f.pack();
f.show();
It can be written in Clojure in a more clear form:
(doto (JFrame.)
(.add (doto (JLabel. "Hello World")
(.setHorizontalAlignment SwingConstants/CENTER)))
.pack .show)
More informations about Java interop and Javascript interop; npm packages can be used via Shadow-cljs.
Created by Rich Hickey in 2013.
A flipped database:
Released by Rich Hickey in 2007 after two years of work. The name Clojure originated in closure of the languages C#, Java and Lisp (CLJ). It is a stable language with a tiny core. There are amazing libraries and tools which increase the productivity. The basic philosophy of Clojure is simplicity. |
Every program needs to preserve the state which changes over time. Clojure does not support variables but gives higher primiteves for working with values changing over time, similarly to SQL databases.
Clojure works with values which cannot be changed. But it is possible to efficiently create modified values.
Rich Hickey created these data structures so they are efficient enought for 99% of all applications. Time to reading is 1-2x, time of writing is 1-4x. Often, the program can be in total more efficient which is counterintuitive.
Atoms allow to work with time. They are always pointing to some value. Values cannot be changed but an atom can be atomically updated to point to a different value. Special functions swap! and reset! change values of atoms.
(def a (atom {}))
@a ; => {}
(def b @a)
(swap! a assoc :abc 123)
@a ; => {:abc 123}
b ; => {}ClojureScript only has atoms because it does not run in parallel. Clojure has further primitives: refs (supporting transactions), agents (asynchronous changes), vars.
Supported directly as language primitives using special notation:
'(1 2 "abc"), typically for code, slow access to a value at the given index.[1 2 :a :b "abc"], support fast random access.{:a 1 :b 23 "abc" 11}, pairs key-value, both can be anything.#{:a :b "abc" 11 7}.These data structures are immutable, very efficient, can contain arbitrary data types and can be arbitrarily nested in each other.
{:source-paths ["src"]
:dependencies [[reagent "1.2.0"]
[re-frame "1.3.0"]
[binaryage/devtools "0.9.10"]]
:nrepl {:port 9000}
:builds {:client {:target :browser
:output-dir "resources/public/js/compiled"
:asset-path "/js/compiled"
:modules {:main {:init-fn example-client.core/init}}
:compiler-options {:infer-externs :auto}
:devtools {:http-root "resources/public"
:http-port 3000
:after-load example-client.core/mount-root
:watch-dir "resources/public"}}}}The documentation. It allows te define shorthands (called local bindings) which improves readablity and allows a value to be used multiple times. It replaces local variables in most languages. The syntax is:
(let [pair binding-symbol,value]
an arbitrary list of forms)
(let [[x y :as my-point] [5 3]]
(println x y)
(println my-point))
5 3
[5 3]
Sequences are abstractions which have the first element and the rest of the sequence. Most functions working with sequences are lazy, so even infinite sequences can be used. For instance, (range) is the infinite sequence of non-negative integers.
All data structures of Clojure can be used as sequences. For instance, a hash map corresponds to a sequence of pairs key-value (in some order). Also Java data structures work as sequences. Basic Clojure functions almost always work with an arbitrary sequence, so the same code can be used for all data structures.
nil - correspons to null.true/false - boolean values.123, 14.23 - numbers.123M - BigInt (only in Clojure) 2/3 - exact ratios (only in Clojure)."abc" - strings.:abc - keywords, similar to strings, but they identify themselves and are typically used as keys in maps. Think of enums.abc - symbols, typically corresponding to something else.#"a.*$" - regular expressions.The Internet runs on text messages. The formats such as HTTP or JSON are usable by everyone. ORM and other binary formats are not.
An idea of Clojure is to use the same techniques for programming the insides of our systems. So distinct parts of the system communicate via plain data.
Data can be easily printed, analysed, a single set of functions can be applied, the communication is transparent. Transforms are applied on data to change them. The name functional programming wrongly puts the transformations into the fronts, the data are more important.
Non-truthy values are nil and false. All other values are truthy: 0, empty collections, etc. Negation is done using not.
Functions =, not= compare values, they work for arbitrary data structures and for an arbitrary number of parameters.
Functions <, <=, >, >= compare numbers. They work for an arbitrary number of parameters: (< 1 2 3 4 5) is true.
Macros and and or accept an arbitrary number of arguments. They return the last/first truthy value. For instance (or val 5) allows to define the default value in the case val is nil.
(def beers (atom 0))
(defn beer-counter [{:keys [name]}]
[:div
[:h1 "Hello, " name]
[:p "You have drank " @beers " beers"]
[:button
{:on-click #(swap! beers inc)}
"Drink a beer"]])
Functions in Clojure are defined using defn:
(defn myfunc [x y z]
(+ (* x y) z))
Functions are called by placing the function symbol and arguments into a list:
(myfunc 2 3 4)
=> 10
Functions are first-class, so they can be arbitrarily stored or passed as values.
Anonymous functions can be created using fn:
(fn [x y]
(+ x y))
A shorthand notation is available for very simple functions:
#(+ %1 %2)
The parameters are %1, %2, etc. For unary functions, % can be used as the argument.
EDN (Extensible Data Notation) is a data format used for programming Clojure. The Clojure compiler reads the format and interprets it as a code. EDN is basically an improved JSON to enable support programming. So you don't have to write commas and aside strings also symbols and keywords are supported. It is also possible to define new data types using the so-called reader literals.
The forms if, when and cond. It is possible to use them anywhere, even inside a form, so they serve as a more readable ?: operator from C as well.
If contains a test, true form and a false form:
(if (< x y)
x
y)
When contains only the true form of an if form, but it can contain an arbitrary number of forms in it.
(when (< x y)
(println "x is smaller")
x)
Cond allows to have an arbitrary number of branches in an elegant syntax. The first true branch is returned, the rest is ignored:
(cond
(< x y) "x is smaller than y"
(< x z) "x is smaller than z"
:else "x is larger than both y and z")
There are useful shorthands if-not, when-not, if-let, when-let.
Overview of core functions in Clojure and Clojurescript.
The high-order function map independently transforms elements of a sequence by the given function.
So (x, y, z, ...) ⇒ (f(x), f(y), f(z), ...).
It even works for multiple input sequences.
(map inc [1 2 3 4]) ; => (2 3 4 5)
(map #(* %1 %2) [1 2 3 4] [0 1 2 3 4]) ; => (0 2 6 12)
They allow to chain a list of transformations, they work as a pipe. So a value is send to the first transform, the result is send to the second, etc. More information in the documentation.
(->> (range 10) ; => (0 1 2 3 4 5 6 7 8 9)
(map #(* % %)) ; => (0 1 4 9 16 25 36 49 64 81)
(filter even?) ; => (0 4 16 36 64)
(reduce +)) ; => 120
I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea. The big idea is "messaging"
When one say functional programming, most programmers recall Haskell. It had infected academia so students are forced to learn it in university. I personally think that Haskell does big harm to functional programming because it is presented as mysterious and hard to understand. Words such as category theory, monads or functors create the following reaction with most programmers:
Clojure is a practical transparent programming language without similar non-sense ideas.
Filtr only keeps elements satisfying a predicate (a unary function) and remove only removes these elements.
(filter odd? [0 1 2 3 4 5 6]) ; => (1 3 5)
(remove odd? [0 1 2 3 4 5 6]) ; => (0 2 4 6)
Compared to other languages, the syntax of for is much more powerful but works independently on each sequence element. It returns a sequence of transformed elements.
(for [x [1 2 3 4 5]]
(* 2 x)) ; => (2 4 6 8 10)
(for [x (range 5)
y (range 5)
:when (< x y)
:let [z (+ x y)]]
[x y z])
; => ([0 1 1] [0 2 2] [0 3 3] [0 4 4] [1 2 3] [1 3 4]
; [1 4 5] [2 3 5] [2 4 6] [3 4 7])
Rich Hickey: Objects are like marionettes. Whoever having the reference fully controls the object. Nothing works in the real world like this.
{:remote-addr "127.0.0.1",
:scheme :http,
:query-params {"somekey" "somevalue"},
:form-params {},
:request-method :get,
:query-string "somekey=somevalue",
:content-type nil,
:uri "/foobaz",
:server-name "localhost",
:params {"somekey" "somevalue"},
:headers {"accept-encoding" "gzip, deflate",
"connection" "close",
"user-agent" "Apache-HttpClient/4.1.2 (java 1.5)",
"content-length" "0",
"host" "localhost:8383"},
:content-length 0,
:server-port 8383,
:character-encoding nil}The function reduce. Elements of a sequence are consumed one by one and cummulate into a returned value. The return value is updated in each step by a given binary function applied on the previous return value and the next element in the sequence.
(reduce + (range 10)) ; => 45
(reduce conj #{} [1 3 7 4 8 3 1 7]) ; => #{1 3 7 4 8}
A for cycle in languages such as C can have many different meaning. Distinct functions are used in Clojure, so the meaning is immediately clear.
for - individual values of a sequence are transformed independently, the computation could be paralelized.
for (int i = 0; i < 10; i++) { | (for [x arr] |
reduce - the values are cummulated, the cycle runs are not independent.
int sum = 0; | (reduce + arr) |
Rich Hickey: This is life sucking ...
Do you want to learn Clojure? We have build an interactive tutorial for it in OrgPad. Code snippet are running Klipse and it is possible to change them. The results are immediately updated.
You can imagine REPL (Read-Eval-Print-Loop) as a communication channel between you and a running Clojure program. This allows a lot of amazing things:
This video shows an example of debugging a simple server using REPL. It feels like having a dialog with your running program.