(inc 1)
;=> 2
If the syntax is good enough for the information, it should be good enough for the meta-information.
| Java | Clojure |
|---|---|
int i = 5; | (def i 5) |
if (x == 1) return y; else return z; | (if (= x 1) y z) |
x * y * z; | (* x y z) |
foo(x, y, z); | (foo x y z) |
foo.bar(x); | (.bar foo x) |
Evaluated as function calls
(inc 1)
;=> 2(+ 1 2)
;=> 3Always in prefix form
()
;=> ()(cons 1 ())
;=> (1)(cons 1 (cons 2 ()))
;=> (1 2)(list 1 2 3)
;=> (1 2 3)[1 2 3 4]
Preferred over lists; easier to write
Order 1 count and lookup by index
(get [10 20 30 40 50] 3)
;=> 40Similar to arrays, but can be added to
(conj [1 2 3] 4)
;=> [1 2 3 4](pop [1 2 3 4])
;=> [1 2 3](assoc [1 2 3 4] 0 5)
;=> [5 2 3 4](vec (list 1 2 3 4))
;=> [1 2 3 4](cons 0 [1 2 3 4])
;=> (0 1 2 3 4)(def a [1 2 3 4])
(= a a)
;=> true(def b [1 2 3 4])
(= a b)
;=> truea and b are different objects |
(identical? a b)
;=> false(= [1 2 3 4] (list 1 2 3 4))
;=> trueDifferent types, but considered equal
Usually lowercase-words-hyphenated
Begin with an alphabet character
Can contain numbers and punctuation
Identifiers
incfoo(quote foo)'fooquote means don’t resolve or evaluate
(quote (1 2))
;=> (1 2)'(1 2)
;=> (1 2)Without quote we have a problem:
(1 2)
;=> Exception: Long cannot be cast to IFn| Tried to apply 1 as a function |
| Type | Value |
|---|---|
Long | 1 |
Double | 3.14 |
BigInteger | 1000000000000N |
BigDecimal | 1000000000000.1M |
Exponents | 1e3 |
Ratio | 2/5 |
"This is a string."
Characters written with a backslash
\a \b \c \newline \tab \space
{"name" "Fate of the Furious"
"sequence-number" 8
"rotten-tomatoes" 0.66
"imdb" 0.67}Order 1 lookup, "add", "delete" by key
Tuned to be fast
Replacement for structs/objects
Versatile; used often in Clojure code
:my-keyword
Shorthand identifiers
Begin with a colon
Often used as keys in hashmaps
{:name "Fate of the Furious"
:sequence-number 8
:rotten-tomatoes 0.66
:imdb 0.67}(get {:a 1} :a)
;=> 1(get {:a 1} :b 2)
;=> 2(assoc {:a 1} :b 2)
;=> {:a 1, :b 2}(dissoc {:a 1} :a)
;=> {}(update {:a 2} :a inc)
;=> {:a 3}(update {:a [1 2 3]} :a conj 4)
;=> {:a [1 2 3 4]}(merge {:a 1} {:b 2})
;=> {:a 1, :b 2}Commas are optional and treated as whitespace
(= {:a 1, :b 2, :c 3}
{:a 1 :b 2 :c 3})Prefer newlines
{:a 1
:b 2
:c 3}{:name "Fate of the Furious"
:sequence-number 8
:ratings {:rotten-tomatoes 0.66
:imdb 0.67}}Ratings are a nested map
{[1 2] {:name "diamond", :type :treasure}
[3 4] {:name "dragon", :type :monster}}A map with vector coordinate keys, and map values
(assoc-in {:a {:b {:c 1}}} [:a :b :d] 2)
;=> {:a {:b {:c 1, :d 2}}}(update-in {:a {:b {:c 1}}} [:a :b :c] inc)
;=> {:a {:b {:c 2}}}(get-in {:a {:b {:c 1}}} [:a :b :c])#{1 2 3}Near constant time lookup
(contains? #{1 2 3} 3)
;=> true(conj #{1 2 3} 4)
;=> #{1 2 3 4}(disj #{1 2 3} 2)
;=> #{1 3}union, difference and intersection are available in the clojure.set namespace
src/training/my_namespace.clj
(ns training.my-namespace
(:require [clojure.set :as set])
(:import (java.time Instant Duration)))
(set/union #{1 2 3} #{3 4})
=> #{1 2 3 4}The name must match path and filename
my-namespace → my_namespace.clj
training. → training/
(require '[clojure.set :as set]) (import (use 'clojure.set) (require '[clojure.set :refer :all])
Avoid use and :refer :all |
ns works in the REPL! |
(ns my.namespace (:require [clojure.set :as set]))
Expressions which are evaluated to results
If an expression needs to be compiled, it will be
Can be loaded from files or evaluated dynamically
Unit of compilation is a form
Nominate an entry point namespace/function
:my.namespace/rect
Shortcut:
::rectangle
;=> :my.namespace/rectangle:: expands to the current namespace
(defn square [x]
(* x x))
(square 2)
;=> 4(defn square
"Multiplies a number by itself"
[x]
(* x x))
(square 2)
;=> 4(def x 1)
;=> #'my.namespace/xx
;=> 1Global mutable reference
Use sparingly
#' means var
(var x)
;=> #'x#'x
;=> #'xThe symbol x resolves to a Var
Vars are automatically dereferenced when evaluated
Dereferrencing returns the value associated with the Var
Avoid using vars like variables
defn is actually def with a function value
Can use #'x or (var x) to access the Var
(let [x 1]
(inc x))
;=> 2Bind symbols to values in a scope
Shadow existing bindings
Prefer let over def
(let [[x y] [1 2]]
(+ x y))
;=> 3Literal data structure containing symbols
Matches structure
(defn normalize1 [v]
(let [x (first v)
y (second v)
length (Math/sqrt (+ (* x x) (* y y)))]
[(/ x length) (/ y length)]))Avoid extracting substructure manually:
(defn normalize2 [[x y]]
(let [length (Math/sqrt (+ (* x x) (* y y)))]
[(/ x length) (/ y length)]))(let [[a b] (list 1 2)]
b)
;=> 2(seq "abc")
;=> (\a \b \c)(seq {:a 1, :b 2, :c 3})
;=> ([:a 1] [:b 2] [:c 3])(seq? 8)
;=> false(let [[a b] "abc"]
b)
;=> \b(drop 2 [0 0 0 0])
;=> (0 0)(range 5)
;=> (0 1 2 3 4)(take 2 "abcd")
;=> (\a \b)Many sequence oriented functions
Never modify the original sequence
Often lazy
Lazy means that the next value in the sequence is only calculated when it is made use of
Stream abstraction; only the currently used item needs to be in memory
Useful for processing files that don’t fit in memory
(for [i (range 10)]
(* i i))
;=> (0 1 4 9 16 25 36 49 64 81)(for [file ["a" "b" "c"]
rank [1 2]]
(str file rank))
;=> ("a1" "a2" "b1" "b2" "c1" "c2")(for [i (range 10)
:when (odd? i)
:let [square (* i i)]]
square)
;=> (1 9 25 49 81)(let [m {:a 1, :b 2, :c 3}]
(for [[k v] m]
[v k]))
;=> ([1 :a] [2 :b] [3 :c])| Destructuring is available in any binding form |
&(defn sub [& vs]
vs)
(sub 1 2 3 4)
;=> (1 2 3 4)Variadic means variable number of arguments
Arity means number of arguments
We could have just passed a vector instead
Calls a function with a sequence of arguments
(apply + [1 2 3 4])
;=> 10Most mathematical functions are variadic:
(+ 1 2 3)
;=> 6(def x {:a 10
:b 20})
(let [{a :a, b :b} x]
(+ a b))
;=> 30(let [{:keys [a b]} x]
(+ a b))
;=> 30(def y {"a" 10
"b" 20})
(let [{a "a", b "b"} y]
(+ a b))
;=> 30(let [{:strs [a b]} y]
(+ a b))
;=> 30(def x (range 5))
(first x)
;=> 0(rest x)
;=> (1 2 3 4)(let [[a & more] (range 5)]
a)
;=> 0(let [[a & more] (range 5)]
more)
;=> (1 2 3 4)(def movie {:name "Fate of the Furious"
:sequence-number 8
:ratings {:rotten-tomatoes 0.66
:imdb 0.67}})
(get-in movie [:ratings :imdb])
;=> 0.67(let [{{:keys [imdb]} :ratings} movie]
imdb)
;=> 0.67(defn f [{:keys [a b] :as x}]
x)
(f {})
;=> {}(defn f [{:keys [a b] :or {a "default"}}]
a)
(f {})
;=> "default"(defn f [x]
(let [defaults {:a "default"}
{:keys [a b]} (merge defaults x)]
a))
(f {})
;=> "default"Removal of next form #_
#_(this form is removed) #_#_ (ignored-1) (ignored-2)
Temporarily remove a form when debugging code
Looks like a bug eyes emoji
#"pattern"
(re-seq #"\w+" "the quick brown fox")
;=> ("the" "quick" "brown" "fox")See manual end of section 2
Set up the new namespace called training.syntax
(ns training.syntax)
Define a var called message bound to the string "greetings"
(def message "greetings")
;=> nilPrint out the value of the var message
(prn message)
;;; "greetings"
;=> nilprn keeps the quotes around strings; println does not |
Create a let binding that binds the symbol message to "well hello there", and prints out message inside the let block:
(let [message "well hello there"]
(prn message))
;;; "well hello there"
;=> nilPrint out message again, outside of the let block:
(prn message)
;;; "greetings"
;=> nilmessage global var is still the original value |
Create a let binding that destructures a map and prints the greeting and tone:
(def m {:greeting "good morning", :tone "happy"})
(let [{:keys [greeting tone]} m]
(prn greeting tone))
;=> "good morning" "happy"Destructure a single map input and return a string combining greeting and tone:
(defn hi [{:keys [greeting tone]}]
(str greeting " - " tone))
(hi m)
;=> "good morning - happy"
Comments
Anything following a semicolon is a comment
Less common is the comment form: