Clojure Book
Clojure Book
Clojure Book
If
If-Let
When
When-Let
Case
Cond
Condp
Boolean
Strings
Literal
Str
Format
Integers
Addition
Subtraction
Multiplication
Division
Modulo
Max
Min
Power
Bigint
Lists
Literal
Conj
How can I remove elements?
Nth
Count
Vectors
Literal
Conj
How can I remove elements?
Nth
.indexOf
Sets
Literal
Conj
Disj
Sort
Contains?
Subset?
Superset?
Maps
Literal
Get
Assoc
Merge
Keys
Vals
Sequences
Seq
First
Rest
Cons
Concat
Map
Reduce
Into
Reverse
Iterate
Range
Repeatedly
Doseq
Take
Take-While
Drop
Drop-While
Filter
Remove
Partition-by
Group-by
Lazy Sequence
For
Recursion
Recur
Loop
Macros
Defmacro
Macroexpand
Quotes
Syntax-Quotes
Unquotes
Unquote-Splice
Threading Macros
->
->>
Delays
Delay
Force
Futures
Future
Deref
Realized?
Promises
Promise
Atoms
Atom
Reset!
Swap!
Thread Safety
Refs
Ref
Do-sync
Alter
Transaction
Java
Instantiation
Method invocation
Many Thanks
These are very basic questions, but enough to start hacking with the new languages.
Recently, I needed to learn this completely new language Clojure but couldn't find what I wanted. So, I decided to create one while
learning Clojure.
Clojure is a functional programming language and learning functional programming languages is sometimes hard if you've only had
experiences with imperative languages. I have paid careful attention to make this page easy to understand for people who don't have
experiences with functional programming languages, so please don't hesitate to read this page even if you don't know anything about
functional programming.
Hopefully, this page helps you learning functional programming and starting to write Clojure!
Hello, world!
user> "h"
"h"
user> 100
100
user> true
true
Run at repl.it
Our first Clojure code is, of course, printing "Hello, world!". Here, we invoke the function println with the argument Hello, world! .
We call the invocation of function applying the function to data in Clojure or other functional programming language.
The entire line of the code (....) is called a form in Clojure. It's also called expression in a general sense, but there is no real
problem to use them interchangeably.
You can think of form as something that returns a value. "h" 100 true are all forms as well.
Bindings
Giving names to values is called assignment in many programming languages. However, we call the mapping between names and
values binding in Clojure.
Symbol
Symbols are used to bind names to values. a b my-cool-function nyncat : they are all symbols in Clojure.
' will prevent a form from being evaluated. We are doing this here because we want to treat symbols as data in order to pass them to
type function.
user> (println a)
aaaaa
nil
user> (println b)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: b in this context, compiling:(NO_SOURCE_PATH:1:1)
If you don't prepend a ' single quote, you are telling Clojure to resolve the symbol. You can obtain bound values by resolving symbols.
When we try to resolve symbols that are not bound to anything, Clojure complains with the exception.
Let
user=> (let [l "light"] (println (str "God said let there be " l)))
God said let there be light
nil
To bind values to names, use let . Let takes a vector which takes a symbol in the first element and a value in the second element.
user=> (println l)
You cannot resolve the symbol outside the let. This behavior is very similar to private variable in other programming languages.
The binding will be immediately available, so each binding can see the prior bindings.
Scope
When Clojure tries to resolve a symbol, the resolution will be done in the scope of the symbol.
Clojure tries to evaluate a because it needs to pass the value to println . a is bound to "aaa" , so "aaa" is printed in your terminal.
Very straight forward.
user> (let [a "aaa"]
(let [a "AAA"]
(println a)))
AAA
nil
Now, let are nested. Like previous example, Clojure tries to resolve a . However, this time Clojure resolves a to "AAA" , instead of aa
a . Each let will create a scope and symbol resolution is done inside the let where the symbol is resolved.
Also notice that the inner let does not override the scope of outer let .
This kind of scope is called lexical scope. For those whom English is not your first language, lexical means words in a sentence. The
scope is lexical because the compiler relies on the physical location of the symbol (word) in a program (sentence) to resolve them.
The resolution look up bubbles up until it finds the binding. The inner let doesn't provide the binding for a , so it bubbles up to the
outer let . This happens because the scope of inner let is wrapped by the scope of outer let .
Clojure complains with Unable to resolve symbol exception when it cannot find the binding inside the given scope.
You probably find the idea of lexical scope very familiar. This is because most modern programming languages use lexical scope.
There is also something called dynamic scope but you probably don't have to know that right now.
Def
You can also bind symbols to values with def . While you can access the symbol only from within the let where it's declared , you can
access the symbol declared with def from anywhere.
The rule of thumb in Clojure is avoiding the use of def as much as possible. def introduces state and abusing state will make our
code difficult to maintain.
Functions
Defn
The first argument is the name of function say-hello , the second argument is the argument of the function [name] , and the third
argument is the function body (println (str "Hello, " name)) .
#' is the reader macro for var and works the exactly same.
Anonymous Function
Functions are first class objects in Clojure. When you say something is a first class object in programming language X, it means that
you can do all the basic operations with the object such as passing it to a function, returned from a function, and binding it to a variable,
etc.
user=> (hello-world-func)
Hello world
nil
You can bind functions to var just like other values. This works just like defn
user=> #(+ 1 1)
#<user$eval2902$fn__2903 user$eval2902$fn__2903@1cc9a623>
user=> #(+ 1 %)
#<user$eval2930$fn__2931 user$eval2930$fn__2931@3e445ad7>
% will be replaced with arguments passed to the function. When the function takes multiple arguments, %1 is for the first argument, %2
is for the second and so on.
user=> (def say-hello (fn [name] (println (str "Hello, " name))))
#'user/say-hello
user=> (def say-bye (fn [name] (println (str "Good bye, " name))))
#'user/say-bye
You can also pass a function to another function. We define two functions and bind to say-hello and say-bye vars. We also define a
generic function and bind to greeting .
user=> (outer1)
this is from outer
nil
user=> (outer2)
this is yet another from outer
nil
We define a function called inner . inner function prints from-outer var which is supposed to be given by the outer function.
We also define two functions, outer1 and outer2 . These functions both call inner but with different arguments.
As a result, even if the from-outer var doesn't change, inner prints different things.
Namespaces
Namespace provides a way to organize different Clojure objects into to logical groups. These logical groups often are called library
and can be used from other namespaces. A namespace is constructed of symbols chained by . . clojure.core , clojure-http.client ,
my.app.example : they are all namespaces.
Create-ns
To create a namespace, use create-ns . However, it is rare to create a namespace with create-ns because there is more handy ns
macro which will be explained later. You need to place a single quote before a namespace in order to stop resolving the namespace
symbol. See Quotes for more details about quoting.
In-ns
user>
Require
One of the important roles of namespace is providing a scope for Clojure objects.
clojure.by.example> (favorite-language)
"Clojure!!"
user> (favorite-language)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: favorite-language in this context, compiling:(*cider-repl localhost*:501:7)
Things that you define in a namespace is not visible from other namespaces by default. As you can see in this example, favorite-lang
uage function is not visible from user namespace.
user> (clojure.by.example/favorite-language)
"Clojure!!"
To load other namespaces, use require . Once you load clojure.by.example namespace, favorite-language will be available from u
ser namespace.
user> (require '[clojure.by.example :as cbe])
nil
user> (cbe/favorite-language)
"Clojure!!"
Sometimes you want to give a different name to a loaded namespace. In such a case, you can surround the namespace by a vector
with :as keyword.
Refer
user> (favorite-language)
"Clojure!!"
You probably don't want to type clojure.by.example everytime you want to call favorite-language function. You can avoid this if you
use refer .
Use
user> (favorite-language)
"Clojure!!"
require loads a namespace and refer refers the namespace. To do these at once, you can use use .
Import
To import a namespace of Java, you need to use import . Please see Java section for more information about how to use Java.
Ns
ns macro creates a new namespace and gives you an opportunity to load other namespaces at the creation time.
(ns example.namespace
(:require [clojure.java.io])
(:use [clojure.data])
(:import [java.util List Set]))
ns can take :require , :use , and :import keyword. They work the same way as the corresponding functions explained above except
you don't need to quote.
Control Flow
If
if takes a predicate ( true or false ) as the first argument. The second argument will be evaluated if the predicate is evaluated to tru
e . The third argument is equivalent to else in many programming languages which is evaluated when the predicate evaluates to fals
e .
If-Let
After testing condition, you often want to use the result of the testing later. if-let binds the evaluated condition to var when it's truthy.
In this example, when positive-number receives a collection which contains positive numbers, the result of (not-empty (filter pos? n
umbers)) will be bound to pos-nums .
The second argument is for else branch. It will be evaluated when the first argument is evaluated to be false.
Note that filter returns an empty sequence when no value matches the condition instead of nil and an empty sequence is not
falsey in Clojure. But, in order to reach the else branch of if-let , pos-nums has to be nil . For this reason, we are using not-empty
which properly returns nil if the sequence is empty.
When
When-Let
There is also when-let which is similar to if-let but does not contain an else branch.
Case
The expression in the last branch will be evaluated if none of the conditions are matched.
Cond
When you want to do similar thing to case but want to write your own test case rather than = , you can use cond . You can write a
different test case in each branch with cond .
Condp
You can use a predicate with condp for condition. In this case contains? is the predicate.
(contains? #{1 2 3} 5) will be evaluated falsey, thus the default branch will be evaluated.
Boolean
user=> true
true
user=> false
false
true and false are values of Boolean type just like in other programming languages.
user> (boolean 0)
true
user> (boolean 1)
true
Strings
Literal
Str
Format
Like many other languages, Clojure supports string formatting with format function. The concat example above can also be achieved
by using format function.
The first argument tells format function the format you want to apply to your strings. %s is called format specifier and it specifies the
type of data to be formatted. The rest of arguments will replace format specifiers.
%.Nf is a format specifier for floating numbers where N specifies how floating points to print.
Integers
Addition
user=> (+ 2 3)
5
Subtraction
user=> (- 10 3)
7
Multiplication
user=> (* 10 2)
20
Division
user=> (/ 4 2)
2
user=> (/ 4 3)
4/3
Modulo
user=> (mod 3 2)
1
user=> (max 1 2 3 4 5)
5
Min
user=> (min 5 4 3 2 1)
1
Power
user=> (power 2 3)
8
Define a function power . reduce takes a sequence generated by repeat and compute * against each element of the sequence and
returns the sum. The sum is used to do * against the next element of the sequence.
Bigint
Lists
Lists are the most basic collection in Clojure which is a dialect of Lisp (List Processing language). However, you don't often use list as
data collection because you have more useful collection data types in Clojure such as vectors or maps.
Literal
user=> '(1 2 3)
(1 2 3)
A list is a simple collection of values. You can create a list by grouping values with parentheses and a single quote ' at the beginning.
user> (1 2 3)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval2843 (NO_SOURCE_FILE:1)
Conj
To add a value to the list, use conj (conj[oin]). Note that the new value is added to the top.
If you are not familiar with the seq library, jump to Sequences to learn more!
Nth
To get a value from the list, use nth with it's index number. Index starts from 0
Count
Vectors
You can think of vectors as a more efficient and useful version of lists. It's more practical to store multiple values in a vector.
Literal
user=> [1 2 3]
[1 2 3]
You can create a vector by grouping values with square brackets. Unlike lists, you don't need ' because vectors will not be evaluated.
Conj
user=> (conj [1 2 3] 4)
[1 2 3 4]
To add a value, use conj (conj[oin]). Note that the new value is added to the end while it is added to the beginning in lists.
Nth
user=> (nth [1 2 3] 1)
2
To get a value from the vector, you need to specify the index of the value.
Vectors have convenient functions to access elements. To get the first and second elements, use first and second .
.indexOf
user=> (.indexOf [1 2 3] 2)
1
You can get the index of a value with .indexOf . The dot before indexOf indicates Java interop to access methods in Java.
user=> (.indexOf [1 2 3] 4)
-1
Another important trait of sets is that the order of values is not guaranteed.
Literal
user=> #{1 2 3}
#{1 3 2}
You can create a set by grouping values with #{} . Notice the order of the values is not maintained.
user=> #{1 2 3 3}
IllegalArgumentException Duplicate key: 3 clojure.lang.PersistentHashSet.createWithCheck (PersistentHashSet.java:68)
You will get an exception when you try to store duplicated value. In this case, 3 is duplicated value.
Conj
Because sets doesn't allow duplicated values, you will see only one 4 in the final set.
Disj
If trying to disj a value that doesn't exist in the set, it returns the original set.
Sort
Sets are unordered collections of values, meaning that the order of values is not guaranteed. To get a sorted order, use sort .
Contains?
Subset?
Superset?
user=> (clojure.set/superset? #{1 2 3} #{1 2})
true
Maps
Maps are key-value data structure to store multiple values.
Literal
You can create a map by grouping values with {} . Although you can use most of Clojure data type as keys, the two most common type
of keys are keyword and string.
Get
When the key of a map is keyword, you can use the keyword just like a function to get the value.
Assoc
Merge
Keys
Sequences
Sequences are data types that store multiple values. You may wonder: What are differences from lists or vectors? Why Clojure has so
many different collection data types?!
Yes, you can use lists and vectors to store multiple values. In fact, lists and vectors are sequences, and other collection data types such
as maps or sets are also sequences.
Sequences are data types that abstract all more concrete data types with unified functions. These functions are called the Seq library
in Clojure.
One virtue of the sequence is that you can call the same function to collections without worrying about what types of collections that
you are dealing with.
Applying map for the map. We are using key function in this case because inc doesn't work with the map.
When you can apply functions of the seq library to a data type, we say the data type is seqable. The examples above work because
lists, vectors, sets, and maps are all seqable collections.
We will see more functions in the seq library in the following sections to get familiar with sequences.
Seq
To construct a sequence, use seq .
The collection data types such as lists, vectors, sets, and maps are all seqable, therefore you can pass any of them to seq .
Seqable data types and seq are what make sequences elegant in Clojure. As long as your data types are seqable, seq will
convert the data to a sequence. This is why you can apply the same functions in the seq library to different collection types
transparently. These seq library functions internally convert passed collection to a sequence and do the right things for you.
You may wonder that returned values look like lists in REPL. However, this is just a matter of displaying and they are actually
sequences.
First
To get the first element from a sequence, use first .
You probably have used first with different collection data types before without knowing first is actually a sequence function.
You can call first with any collection data types (string is a collection of characters) and get expected behavior because first is a
sequence function and all of these data types are seqable.
Rest
To get all elements except the first one from a sequence, use rest .
Here we can see another important trait of sequences: sequence function always returns a sequence no matter of what types of
collection it takes.
Cons
user=> old-seq
(1 2)
user=> new-seq
(0 1 2)
The operation is equivalent to construct a new sequence by adding an element to the existing sequence, therefore cons (cons[truct]).
Concat
Map
user=> (map inc [ 1 2 3 ])
(2 3 4)
If you want to do something more complex with each element, you can pass an anonymous function where each value is bound to x .
Reduce
reduce boils down elements in a sequence into a single value by applying a function.
The way reduce works is that it first takes out the first two elements from the sequence and apply the function to get a result. Then
applying the same function to the result with the third element and keeps doing the same until the end of the sequence. Because of this
nature, the function must take two arguments.
Of course, you can pass an anonymous function to do more complex stuff. Just don't don't forget that the anonymous function must
take two arguments.
Into
To insert all elements of a sequence into another sequence, use into .
Because of the nature, into is frequently used to convert from one collection type to another.
Reverse
Iterate
user=> (iterate + 0)
You can get a sequence of infinite integers with iterate . Be careful, though. Running this example will freeze your terminal since the
evaluation of this expression never returns.
Range
You can get integers by every x within the range. In this case, we get a sequence of integers at every 5.
Repeatedly
To repeat something over and over again, use repeatedly . We are passing an anonymous function (fn [] (println "hi!")) because
the second argument must be a function.
Doseq
Clojure doesn't have for or for-each . Do something to each element of a sequence, use doseq .
You can bind multiple values. In this case, each element in the first vector is added to each element of the second vector.
Take
Take all elements from a sequence if the size of the sequence is smaller than n .
Take-While
To get the first n elements from a sequence as long as the condition is satisfied but stop taking when the condition is not met, use tak
e-while . neg? returns true for negative number.
Note: Taking elements that only satisfies the condition is not what take-while does. That's the job of select .
Drop
drop is probably the most primitive way to remove elements from a sequence. drop will remove the first n elements.
Drop-While
To get the first n elements from a sequence as long as the condition is satisfied but stop dropping when the condition is not met, use d
rop-while .
Filter
You can remove elements that match the rule you specify from a sequence with filter .
user=> (filter pos? [-1 2 3])
(2 3)
Here is an example to remove positive numbers from a sequence. In this case, being a positive number is the rule that you specify.
The rule is called predicate. Predicates are functions that return boolean values such as pos? .
You can construct your own predicate with anonymous functions. In this example, we are removing elements that are 2 .
Remove
You can remove elements that matches a predicate with remove . The difference from filter is that returned value is what's removed.
In this example, we remove positive numbers from a sequence. The returned values are negative numbers.
Partition-by
To split a collection and group together in a certain way, or in other word partition, use partition . In this example, we partition the
vector into two groups: one smaller than or equal 3 and another bigger than 3.
Notice that (1 2 3) at the end of the sequence is grouped together as a separate sequence from the first one. partition-by doesn't
merge values.
Group-by
user=> (group-by #(< 3 %) [1 2 3 4 5 6 1 2 3])
{false [1 2 3 1 2 3], true [4 5 6]}
group-by splits a collection and does merge them together unlike partition-by . group-by returns a map where key is the result of the
grouping condition.
Lazy Sequence
Most of Clojure's sequences are lazy. All familiar functions such as map range reduce etc returns lazy sequences.
(iterate inc 0)generates a sequence of infinite numbers which, of course, takes infinitely. But, you see println starts printing the
numbers (0 1 2 3 ...... . If the generation of the sequence never ends, how println can even start printing these numbers?
This is possible because iterate generates lazy sequence and println is able to handle lazy sequence correctly. println asks a
number to print from iterate one by one, rather than asking the entire sequence. iterate only computes numbers as it is requested
and pass the numbers to println .
take only asks the first n values from lazy sequence. iterate also only computes the first five numbers because that's what asked
by take .
For
If you are looking for how to write a loop in Clojure, I'm sorry, but this is not what you are looking for. Clojure doesn't have an imperative
loop because there is no mutable local variable in Clojure. Please see the loop section for more information.
In Clojure, for is list comprehension. What is list comprehension? First of all, let's look at an example.
In short, list comprehension is a way to create a list from existing lists. The idea of list comprehension comes from the world of math.
It's used in order to write sets in simpler and easier way.
For example, {x | x >0} means the set of all x that is bigger than than 0. So if x is the set of -1, 1, and 2, then the notation refers to
the set of 1 and 2 but not -1.
This is a list comprehension that means the same thing as {x | x >0} in math.
:when modifier evaluates the body only for values where the predicate is true.
user=> (for [x [0 1 2 3 4 5]
:let [y (* x 3)]
:when (even? y)]
y)
(0 6 12)
while modifier stops the evaluation of the body when the predicate is false.
for iterates collections in a nested fashion. It's useful to create a combination of all elements in given collections.
Recursion
Function is recursive when the function calls itself inside it's definition. This is the most simple way of doing recursion.
We will start from the example of fibo-recursive function that computes Nth Fibonacci number in the Fibonacci sequence because
writing function that computes the Fibonacci numbers is a recursive programming version of hello world.
The Fibonacci sequence is consisted of numbers characterized by the fact that every number after the first two is the sum of the two
preceding ones. 0 1 1 2 3 5 8 13 .... are the beginning of the sequence.
user=> (fibo-recursive 0)
0
user=> (fibo-recursive 6)
8
As you can see, we are calling fibo-recursive function inside the function body of fibo-recursive function. Calling the function inside
the function body is the most basic way to do recursive programming in Clojure and many other programming languages.
Recur
The simple recursion, calling itself inside it's definition, is not the only way to make recursive function in Clojure. recur is a handy tool
to do recursion.
#'user/fibo-recur
user> (fibo-recur 6)
8
We can write a Fibonacci function by using recur as well. recur re-binds it's arguments to new values and call the function with the
new values.
user> (count-down [] 5)
[5 4 3 2 1]
Here is another example of the use of recur . It will keep calling count-down function with updated arguments until n becomes 0.
Why do we have recur when you can write a recursive function with the simple recursion like we do in fibo-recursive ? One of the
most important reasons is the performance optimization.
You cannot compute large Fibonacci number with fibo-recursive . When you try to do that, you will get StackOverflowError.
This is because, with simple recursion, each recursive call creates a stack frame which is a data to store the information of the called
function on memory. Doing deep recursion requires large memory for stack frames, but since it cannot, we get StackOverflowError.
Although we don't go deeply into details, one of techniques to avoid this problem is making your function tail recursive. A function is
tail recursive when the recursion is happening at the end of it's definition. In other words, a tail recursive function must return itself as
it's returned value. When you use recur , it makes sure you are doing tail recursion.
In fact, you will get an error when you try to call recur not at the end of a function.
Because recur does tail recursion, you don't get StackOverflowError with big Fibonacci number although it takes very long time to
compute.
Loop
Does Clojure have for/while loop? No, Clojure doesn't provide a way to write an imperative loop because there is no mutable local
variable in Clojure. However, you can use loop to write code that works like an imperative loop.
user> (count-up 5)
Counting 0
Counting 1
Counting 2
Counting 3
Counting 4
Done!
nil
Hopefully, this code looks similar to a simple counting loop in non-functional programming languages you've had experienced with
before. In this example, recur increments count at the end of each loop and loop uses it in the next loop.
loop is always used with recur and provides a recursion point for recur . A recursion point is a function entry point that recur can
go back to do recursion. However, recur doesn't necessary need loop to do it's job as long as a recursion point is provided.
user> (count-up-no-loop 0 5)
Counting 0
Counting 1
Counting 2
Counting 3
Counting 4
Done!
nil
You can rewrite count-up function without loop . In count-up-no-loop , the recursion point for recur is the function itself. Note that re
cur takes two arguments now. This is because the number of arguments of recur must match that of it's recursion point function.
One final note: loop/recur is merely a friendly way to write recursion code. All imperative loops can be converted to recursions and all
recursions can be converted to loops, so Clojure chose recursions. Although you can write code that looks like an imperative loop with
loop/recur , Clojure is doing recursion under the hood.
Macros
Clojure's Macros gives you the power to restructure your Clojure code as you like. For example, you can create your own code syntax,
invent new control flow, new types of values, etc.
Defmacro
;; Error
user=> (defmacro unless [test then]
"Evaluates then when test evaluates to be falsey"
(list if (list not test)
then))
To define a macro, use defmacro . Like function, you can give it a name, docs, and arguments. Note that you are using quotes '
followed by if and not . This is because you don't want them to be evaluated when you define the macro.
Macros are replaced with Clojure code before it's evaluated. To see how it will be replaced without actually evaluating the macro, use m
acroexpand . Note that you have to use ' because you want it to be unevaluated list.
Quotes
user=> (+ 1 2)
3
user=> '(+ 1 2)
(+ 1 2)
Without a quote, this expression will be just evaluated and returns the value.
However, when an expression is surrounded by quote , it does not evaluate the expression but returns the expression itself.
' is another form of quote . It does the exactly same thing with quote . ' is used more often than quote since it's concise.
You can see quoting at work in macros. In this unless macro, you need to use ' followed by if and not because you don't want
them to be evaluated inside the macro definition.
You need to quote clojure.string namespace otherwise Clojure tries to resolve the namespace symbol and get error. This is because
resolving symbol is the default treatment but clojure.string symbol is not bound to a value.
Syntax-Quotes
user=> `(+ 1 2)
(clojure.core/+ 1 2)
Syntax quoting ` works very similarly to quoting ' : it returns an unevaluated expression.
However, you see the difference from quoting when the expression contains symbols. Unlike quoting, syntax-quoting returns the fully
qualified namespace. Using fully qualified namespace is very important in order to avoid name conflicts when defining macro.
Unquotes
You will see another difference between syntax quoting and quoting when syntax quoting is used with unquoting ~ . Syntax quoting
allows unquoting to evaluate the expression followed by ~ .
The ~@ unquote splice works just like ~ unquote, except it expands a sequence and splice the contents of the sequence into the
enclosing syntax-quoted data structure.
Threading Macros
Threading Macros are macros that helps you to write nested forms in a cleaner and more readable way. Despite it's name, threading
macros are nothing to do with threads in the parallel computing.
->
-> is called thread-first macro. It's first because it's passing down the evaluation of former forms to the first argument of preceding
forms.
Suppose if you want to start from an empty vector and adding numbers to the vector one by one. Here is nested version of the code.
As you add more numbers, the nesting gets deeper and makes your code harder to read. The thread-first macro solves this nesting
problem.
user> (-> []
(conj 1)
(conj 2)
(conj 3))
[1 2 3]
The first argument is the initial value that you want to start from. After the first argument is evaluated, it is then passed to the first
argument of (conj 1) . This is equivalent to (conj [] 1) . The evaluated value is then passed to to the first argument of (conj 2) .
This is equivalent to (conj [1] 2) . Finally, we are evaluating (conj [1 2] 3) which returns [1 2 3] .
->>
->> is called thread-last macro. It's last because it's passing down the evaluation of former forms to the last argument of preceding
forms.
map is an example of such function that takes a collection in the last argument and apply the function in the first argument.
This code converts country names to upper case and say hello to the countries. The vector of country names are passed to the last
argument of the first map which is equivalent to (map clojure.string/upper-case ["japan" "china" "korea"]) . Then it's passed to the
second map which is equivalent to (map #(str "Hello " %) ["JAPAN" "CHINA" "KOREA"]) .
Delays
Delay
When you want to defer the evaluation of an expression, use delay .
This is the example of immediately evaluating an expression. Nothing special is involved here. (do ...) is executed immediately and
it's return value is bound to later var.
When you use delay , the expression is not evaluated immediately, so "Adding" is not printed.
Force
user> (later)
"Adding"
3
You can achieve the same thing by using an anonymous function and def . Then, why do we get bothered with delay?
The difference from a plain function is that delay is only evaluated once and caches the result. "Adding" is only printed once because
delay returns cached result from the second time.
Futures
Future
user=> (do
(Thread/sleep 3000)
(println "hello"))
"hello" is printed after sleeping 3 seconds. This is very obvious because these lines of the code are executed synchronously.
user=> (do
(future
(Thread/sleep 3000)
(println "after sleep"))
(println "hello"))
hello
nil
after sleep
If you use future , (println "hello") is evaluated immediately, and after three seconds, (println "after sleep") will be evaluated.
This is because Clojure puts the expression grouped by future into another thread and moves the current thread forward.
Calls inside future still blocks. So, in this case, "after sleep" is printed after 3 secs.
Deref
See the returned value #<core$future_call$reify__6320@142cbba: 2> which is not what you want. This returned value is the current
state of the future, not the returned value of (inc 1)
To obtain the returned value of (inc 1) , you need to dereference the future with deref .
user> (deref (future (Thread/sleep 1000) "I made it!") 2000 "Can't wait anymore!")
"I made it!"
user> (deref (future (Thread/sleep 3000) "I made it!") 2000 "Can't wait anymore!")
"Can't wait anymore!"
You can tell deref how long you want to wait along with a value to return if it does time out.
Finally, you can do a useful thing by combining future and deref . You can run multiple time consuming tasks in different threads and
block until they finish.
Realized?
user> (repeatedly 6
(fn []
(println (realized? my-future))
(Thread/sleep 1000)))
#'user/my-futurefalse
false
false
false
false
true
To know if a future is already done, use realized? .
Promises
Promise
When you want to defer the evaluation of expressions until you obtain values to pass to them, use promise . The easiest example why
you want to use promise is implementing a callback.
Creating a listener that listens to the promise and fire the callback when a value is delivered to the promise. Just like future, promise will
block when you dereference it.
Now let's start the listener and wait for the time consuming job. After being blocked by the dereference of @my-promise for 5 seconds,
you will see the callback is fired.
Atoms
Atom
You've might hear this statement before: there is no state in Clojure. Thus, the language is impractical and cannot be used to build real
applications. However, this is not true. Clojure has built-in mechanisms to manage application state. Atom is one of the mechanisms.
Use atom to create an atom that points to a value. You can create an atom of any values.
user> @atom-int
53
Reset!
user> @atom-int
35
You can set the value of an atom with reset! . It is used when you are setting the value without regard for the current value, normally
the first time you create the atom.
user> @atom-int
100
Atoms are mutable, so you can update as many times as you want.
Swap!
swap! allows you to use a function to update the value of an atom.
user> @atom-int
"not int"
The function that you pass to swap! will take an argument which is the current atom.
Thread Safety
Atoms are very similar to mutable variables in other programming languages. You can assign value to an atom and update anytime you
want. However, Clojure's atom has one big advantage over them: it's thread safe .
user> (def x 0)
#'user/x
user> (repeatedly 10
(fn [] (def x (inc x))))
(#'user/x...
user> x
10
This will update x ten times and increment x by 1 every time. The final value of x will be 10.
user> (def x 0)
#'user/x
user> (repeatedly 10
(fn [] (future (def x (inc x)))))
(#<core$future_call$reify__6320@410e4786: :pending> #<core$futur...
user> x
5
Similarly, this will update x ten times and increment x every time like the previous example. However, with this code, (def x (inc
x)) will be executed in parallel on different threads because we are using future . When you do this, the final value of x will not be
deterministic anymore. Sometimes it is 5, and sometimes 9 because each thread access and update the same x in its own timing.
user> (repeatedly 10
(fn [] (future (swap! x inc))))
(#<core$future_call$reify__6320@632796c6: :pending>...
user> @x
10
Now atom comes to rescue. x is atom and we use swap! to update the value. Unlike vars, atom is thread safe, so x will be updated
by one thread at one time. Thus, the final value of x is guaranteed to be 10. This is achieved thanks to the Clojure's use of compare-
and-swap in atom.
Refs
Ref
While Atom is handy to manage a state in a consistent way, Ref allows you to manage multiple states while ensuring they are
consistent.
user> @my-ref
0
Do-sync
user> @my-ref
0
user> (dosync
(ref-set my-ref 1)
(ref-set my-ref 2))
2
user> @my-ref
2
The update of refs must be done inside dosync block. dosync is telling Clojure where the transaction update starts from. To set a ref to
a new value, use ref-set .
Any updates to refs always has to be done inside dosync in order to make transactional updates. Otherwise, Clojure complains with N
o transaction running exception.
Alter
alter allows you to use a function to update the value of a ref.
user> (dosync
(alter my-ref
(fn [current_ref]
(inc current_ref))))
1
user> @my-ref
1
user> (dosync
(alter my-ref
(fn [_] "not int")))
"not int"
user> @my-ref
"not int"
The function that you pass to alter will take an argument which is the current ref.
The ref is updated by the return value of the function.
user> (dosync
(alter my-ref multiple-by 10))
1000
You can pass a function that takes multiple arguments. The first argument of the function is the current atom.
Transaction
This piece of code demonstrates how transaction works.
user> (dosync
(alter user merge {:name "Kim"})
(throw (Exception. "something wrong happens!"))
(alter user merge {:age 32}))
Exception something wrong happens! user/eval2997/fn--2998 (NO_SOURCE_FILE:2)
user> @user
{}
Suppose we are trying to create an user record in database. Each alter tries to update user-record ref with user info and you want the
ref to be updated only when both alter succeed.
But, let's assume something wrong occurs between the first and the second alter.
As you see, the user-record ref is still empty. This is because alter inside dosync doesn't update the ref until getting out of dosync
block successfully.
user> (do
(swap! user-record merge {:name "Kim"})
(throw (Exception. "something wrong happens!"))
(swap! user-record merge {:age 32}))
Exception something wrong happens! user/eval3024 (NO_SOURCE_FILE:3)
user> @user-record
{:name "Kim"}
This is the atom version that doesn't work. As you see, user-record atom is half updated when there is the exception.
user> (future
(dosync
(alter my-ref inc)
(Thread/sleep 5000)))
#<core$future_call$reify__6320@6ef7be6a: :pending>
;; Wait 5 seconds
user> (println @my-ref)
1
The other way to see how transaction works is trying to observe the value of ref outside dosync block.
We use future to run the whole transaction in the separate thread and wait two seconds before exiting the dosync block.
The value of the ref is still 0 at this moment because the update to the ref is still not committed.
Java
One of the great traits of Clojure is that you can use Java code from your Clojure code. This trait is called Java interop. Although
Clojure has very rich standard libraries, sometimes you cannot find libraries that you need to solve your problems. If the library exists in
Java, you can borrow it from your Clojure code.
Instantiation
You can create an instance with new which takes class name as first argument.
user> (java.util.Date.)
#inst "2017-01-15T08:17:02.580-00:00"
There is also . form available. . must be placed at the end of class name.
You can also bind Java's instance just like Clojure's value.
Method invocation
Clojure doesn't provide an exponential calculation function in the standard library, so let's borrow pow method from Math class.
user> (Math/pow 2 3)
8.0
You can call Java's method just like Clojure's function. Here is how to call pow class method of Math class. The class method
invocation takes a form of (Classname/Method) .
The instance method invocation takes a form of (.MethodName Instance Args) . This example is equivalent to current_date.toString
() in Java.
If you want to call a method that takes arguments, you can pass them after an instance. This example is equivalent to date1.equals(da
te2) in Java.
Many Thanks
Clojure from the ground up
Programming Clojure
Clojure Cheatsheet
And many other great articles and pages made by the Clojure community.