Other Random Staff about clojure
Table of Contents
Some interesting projects:
- https://github.com/ring-clojure/ring
- http://liquidz.github.io/
- https://github.com/liquidz/misaki
- http://nakkaya.com/
1 A first try
1.1 Setup
First, install clojure and lein2.
To start a new project:
lein new myproj cd myproj lein repl
This will install dependencies and enter repl.
1.2 Type
Since clojure sits on top of JVM, the type seems to be the same, and
you can get the type of something by the function type
.
user=> (type 3) ;; java.lang.Long user=> (type :cat) clojure.lang.Keyword
A list of types
- java.lang.Long
- (type 3)
- clojure.lang.BigInt
- (type 5N)
- java.lang.Integer
- (type (int 0))
- java.lang.Short
- (type (short 0))
- java.lang.Byte
- (type (byte 0))
- java.lang.Double
- (type 1.23)
- java.lang.Float
- (type (float 1.23))
- java.lang.String
- (type "cat")
- clojure.lang.Symbol
- (class 'str)
- clojure.lang.Keyword
- (type :cat)
- clojure.lang.PersistentList
- (type '(1 2 3))
- clojure.lang.PersistentVector
- (type [1 2 3])
- clojure.lang.PersistentHashSet
- #{:a :b :c}
- clojure.lang.PersistentArrayMap
- {:name "mittens" :weight 9 :color "black"}
1.3 Function
- let
- fn
- lambda function
- def
- symbol
- defn
- define a named function
1.4 Macro
- defmacro
1.5 State
1.5.1 delay
Basically this is an abstraction of function: define a function will
not evaluate function body. So delay works similar: the call to delay
returns an Delay Object, which can be called on by deref
to evaluate
the body.
(def later (fn [] (prn "Adding") (+ 1 2))) (later) ;; => print output (def later (delay (prn "Adding") (+ 1 2))) (deref later) ;; => print output
The difference between a delay and a function:
- function evaluate every time it is called
- delays only evaluate their expressions once. They remember their value, after the first evaluation, and return it for every successive deref.
The delay opeartor: @later
is equivalent to (deref later)
.
1.5.2 Future
This is for parallel.
I don’t need the result of evaluating these expressions yet, but I’d like it later. Could you start working on it in the meantime?
(def x (future (prn "hi") (+ 1 2))) (deref x) ;; => 3
The body of future is executed in a new thread in parallel! Deref it will get the value of the last expression. Like delays, the expressions are only evaluated once.
1.5.2.1 Atom
The parallel program brings thread-safe problem: modify something at
the same time in different threads. Clojure has a atom
function to
protect a data and ensure its thread-safety.
Use reset!
to set value of atom, and use swap!
to update.
(def xs #{}) (dotimes [i 10] (future (def xs (conj xs i)))) user=> xs ;; => #{1 4 5 7} (def xs (atom #{})) (dotimes [i 10] (future (swap! xs conj i))) user=> @xs ;; => #{0 1 2 3 4 5 6 7 8 9}
1.5.2.2 Ref
Atom is linearizable, but not serializable: it does not guarantee orders. Ref is serializable.
Use ref-set
to set value of a ref, and use alter
to update. They
must be in a dosync
block, and the block order is guaranteed.
user=> (def x (ref 0)) user=> (def y (ref 0)) user=> (dosync (ref-set x 1) (ref-set y 2)) 2 user=> [@x @y] [1 2] user=> (def x (ref 1)) user=> (def y (ref 2)) user=> (dosync (alter x + 2) (alter y inc)) 3 user=> [@x @y] [3 3]
If some of the refs do not need order, you can boost the program by
release that, using commute
:
user=> (dosync (commute x + 2) (commute y inc))
Finally, you can use ensure
to update one ref using another,
guaranteeing order.
user=> (dosync (alter x + (ensure y)))
1.5.3 Promise
Delays defer evaluation, and futures parallelize it. What if we wanted to defer something we dont even have yet? To hand someone an empty box and, later, before they open it, sneak in and replacing its contents with an actual gift?
(def box (promise)) (deref box) ;; empty (deliver box :live-scorpiojns!) (deref box) ;; => live-scorpiojns! (deliver box :puppy) ;; => nil (deref box) ;; => live-scorpiojns!
Some highlights:
- box contains nothing initially
- can be delivered
- cannot be re-delivered