Clojure is a great language, and as every language, it has some unobvious and strange things, which seem confusing.
Some of such things are gathered here. Most of them encountered by stupidity, docs misunderstanding, clojure specifics and procrastination.
(contains? [10 20 30] 20) => false
Surprised?
The issue here is contains?
function
checks if key (not a value!) is present in collection.
For vector - key is index, and obviously there is no 20th element.
If you need to check if value is present in collection,
use some
predicate:
(some #(= % 20) [10 20 30]) => true
Keep in mind this function works in linear time. If you need to call it often, build a set and use it as a function.
(def numbers (set [10 20 30]))
(numbers 20) => 20 ;; treated as true
(numbers 1) => nil ;; treated as false
Note: if set contains nil
or false
, it won’t work.
More discussion here.
You need stack. In clojure you can treat list
like a stack.
;; take stack head
(peek '(1 2 3)) => 1
;; pop stack
(pop '(1 2 3)) => (2 3)
;; push item into stack
(cons 0 '(1 2 3)) => (0 1 2 3)
Everything is fine. But if you call pop
after cons
…
ClassCastException clojure.lang.Cons cannot be cast to clojure.lang.IPersistentStack
The problem is cons
is type destructive operation.
(type '(1 2 3)) => clojure.lang.PersistentList
(type (cons 0 '(1 2 3))) => clojure.lang.Cons
And clearly, there is no pop
defined for Cons
type.
To preserve collection type, use conj
function.
(type '(1 2 3)) => clojure.lang.PersistentList
(type (conj '(1 2 3) 0)) => clojure.lang.PersistentList
What’s the difference between
(last [1 2 3])
and (last '(1 2 3))
?
Functionally there is no difference, both snippets return 3
Moreover, from performance view there is no difference as well. Both snippets work in linear time. Linear. O(n). Even for vector.
What’s the difference between
(count [1 2 3])
and (count '(1 2 3))
?
Much better here, count
works in constant time for vector
and linear for list. So, if you really need fast last
for vector,
use count
.
;; 10M size vector
(def v (vec (range 1e7)))
(time (last v)) => 9999999
;; "Elapsed time: 1704.172859 msecs"
(time (get v (dec (count v)))) => 9999999
;; "Elapsed time: 0.087302 msecs"
Huge difference.
Do not use vector specific functions
to list. Also, consider using mapv
instead of map
when processing vectors.
Everybody knows reduce
and apply
functions.
Sometimes using choosing between them does not make any difference.
;; 100K numbers
(def N (repeatedly 1e6 #(rand-int 100)))
(time (reduce + N)) => 49503683
;; "Elapsed time: 311.496332 msecs"
(time (apply + N)) => 49503683
;; "Elapsed time: 339.624222 msecs"
Sometimes it does.
;; 10K strings
(def S (repeatedly 1e5 (fn [] "1")))
(time (count(apply str S))) => 100000
;; "Elapsed time: 35.027218 msecs"
(time (count(reduce str S))) => 100000
;; "Elapsed time: 24249.845948 msecs"
Conditionals are horses of programming language.
(def status 500)
(cond (= status 403) "login.html"
(= status 404) "not_found.html"
(= status 500) "server_fault.html"
(= status 200) "index.html"
:else "something_wrong.html")
Pretty straightforward, but a piece (= status %)
look ugly.
Let’s refactor it a bit and avoid repetition with condp
(def status 500)
(condp = status
403 "login.html"
404 "not_found.html"
500 "server_fault.html"
200 "index.html"
:else "something_wrong.html")
Much better. Except we broke previous functionality. If pass invalid status (let’s say 201) we get an exception:
IllegalArgumentException No matching clause: 201
And that’s right: sugared keyword :else
in cond
behaves like regular clause in condp
To make working solution, just remove :else
.
(condp = status
403 "login.html"
404 "not_found.html"
500 "server_fault.html"
200 "index.html"
"something_wrong.html")
Get this portable, pure, thread-safe, well-documented, so beatiful and very awesome function:
(defn add [a b]
"Performs addition operation.
More about addition here: http://en.wikipedia.org/wiki/Addition"
(+ a b))
What’s wrong here? Is it really well-documented?
If you have sources, then yes, you see all documentation.
But if you are library user you have no idea what function add
might do.
(doc add)
; -------------------------
; user/add
; ([a b])
; nil
Misplaced docstring. Transpose it with arglist.
(defn add
"Performs addition operation.
More about addition here: http://en.wikipedia.org/wiki/Addition"
[a b]
(+ a b))
(doc add)
; -------------------------
; user/add
; ([a b])
; Performs addition operation.
; More about addition here: http://en.wikipedia.org/wiki/Addition
Note: clojure lint tool eastwood is able to find this mistake.
There is less known, but useful feature dynamic binding
(def ^:dynamic *DEBUG* false)
If your code depends on *DEBUG*
binding,
its behaviour can be modified without changing
function definition
(defn square [a]
(when *DEBUG* (println "call square"))
(* a a))
(square 10) => 100
(binding [*DEBUG* true]
(square 10))
;; call square
=> 100
If you have another value, that depends on dynamic binding, it is not dynamic.
(def ^:dynamic *DEBUG_PREFIX*
(if *DEBUG* "DEBUG=TRUE" "DEBUG=FALSE"))
(binding [*DEBUG* true]
*DEBUG_PREFIX*)
=> "DEBUG=FALSE"
To fix that make it as a no-arg function. It will obtain dynamic binding right after the call.
(defn- debug-prefix []
(if *DEBUG* "DEBUG=TRUE" "DEBUG=FALSE"))
(binding [*DEBUG* true]
(debug-prefix))
=> "DEBUG=TRUE"
There are three ways to access map value
(def m {:name "Quentin Tarantino"})
;; 1. By keyword
(:name m) => "Quentin Tarantino"
;; 2. By map as a function
(m :name) => "Quentin Tarantino"
;; 3. Using get
(get m :name) => "Quentin Tarantino"
What version to use is a matter of style, because they are almost always interchangeble, but keep in mind some caveats.
First approach is bad, when your keys are strings or whatever-not-a-function.
(def m {"name" "Quentin Tarantino"})
("name" m) => ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
Second one is able to handle that, but instead it can throw NPE, when map is nil
(def m nil)
(m :name) => NullPointerException
Using get
we avoid these pros, and access become more obvious.
I prefer first version when using keyword explicitly, otherwise access via get
Thread macro is a convenient way to implement sequential processing.
Thread First ->
inserts previous result
as a first argument for the next expression.
(-> (http/get URL)
(validate-status)
:body
:data
(first))
Thread Last ->>
does the same,
but insert previous result as a last argument
(->> (range 100)
(map square)
(filter even?)
(reduce +))
If you need to insert previous result into specific position, wrap next expression to one-arg anonymous functions.
(->> (http/get URL)
(#(validate-status % :throw-exception))
:body
:data
(#(conj % [:guard])))
This one is not related to clojure, but interesting. Consider problem:
Find all words in a string
Pretty obvious, huh?
(re-seq #"\w+" "Hello, world!") =>
("Hello" "world")
We don’t want numbers and underscore appear inside word
(re-seq #"\w+" "Hello, w_orld 1984!") =>
("Hello" "w_orld" "1984")
Okay, use just letters:
(re-seq #"[a-zA-Z]+" "Hello, w_orld 1984!") =>
("Hello" "w" "orld")
No, we don’t want broken words. Use \b
word boundary regex.
(re-seq #"\b[a-zA-Z]+\b" "Hello, w_orld 1984!") =>
("Hello")
Solved?
Not for ukrainian
(re-seq #"\b[a-zA-Z]+\b" "Привіт, світ 1984!") =>
nil
And for every language which contains non-ascii letters in alphabet.
Use unicode letter regexp \p{L}
instead.
(re-seq #"\b\p{L}+\b" "Привіт, світ 1984!") =>
("Привіт" "світ")