TOC
- Chapter 1 Down the Rabbit Hole
- Chapter 2 Functional Programming
- Chapter 3 Collections and Data Structures
- Chapter 4 Concurrency and Parallelism
- Chapter 5 Macros
- Chapter 6 Datatypes and Protocols
- Chapter 7 Multimethods
- Chapter 8 Organizing and Building Clojure Projects
- Chapter 9 Java and JVM Interoperability
- Chapter 10 REPL-Oriented Programming
- Chapter 11 Numerics and Mathematics
- Chapter 12 Design Patterns
- Chapter 13 Testing
- Chapter 14 Using Relational Databases
- Chapter 15 Using Nonrelational Databases
- Chapter 16 Clojure and the Web
- Chapter 17 Deploying Clojure Web Applications
- Chapter 18 Choosing Clojure Type Definition Forms Wisely
- Chapter 19 Introducing Clojure into Your Workplace
- Chapter 20 What’s Next?
Chapter 1, Down the Rabbit Hole
in browser Clojure implementation: http://trycli.com
(android has a Clojure REPL app)
start repl on command line
% java -cp clojure-1.4.0.jar clojure.main
(to exit repl, enter Ctrl-D
)
Expressions, Operators, Syntax, and Precedence
All Clojure code is made up of expressions, each of which evaluates to a single value.
Clojure call expressions follow one simple rule: the first value in a list is the operator, the remainder are parameters to that operator.
Homoiconicity
Clojure code is composed of literal representations of its own data structures and atomic values; this characteristic is formally called Homoiconicity, or more casually, code-as-data.
Clojure (like all Lisps): rather than defining a syntax that will be transformed into an AST (Abstract Syntax Tree), Clojure programes are written using Clojure data structures that represent that AST directly.
The Reader
read
read-string
pr
pr-str
Scalar Literals
Strings:
"hello"
Booleans:
true
, false
Null:
nil
Characters:
\a
- \space- - \newline
- \formfeed
- \return
- \backspace
- \tab
*Keywords:*
(def person {:name "Sandra"
:city "Portland"})
:name
, :city
are keywords. keywords are functions what look themselves up in collections passed to them. keywords are always prefixed with a colon :
, and can otherwise consist of any nonwhitespace character.
a slash character /
denotes a namespaced keyword, while a keyword prefixed with two colons ::
is expanded by the reader to a namespaced keyword in the current namespace.
keywords are one type of "named" values
(name :user/location)
;= "location"
(namespace :user/location)
;= "user"
the other named type of value is the symbol
Symbol:
symbols must begin with non-numeric character, and can contain *
, +
, !
, -
, _
, and ?
and any alphanumberic characters.
symbols that contain a slash /
denote a namespaced symbol.
Numbers:
(check doc)
Regular expressions:
strings prefixed with a hash character #
as regular expression literals
(class #"(p|h)ail")
;= java.util.regex.Pattern
Comments:
- single line: prefixing with a semicolon
;
- form level: using the
#_
macro
(read-string "(+ 1 2 #_(*2 2 ) 8)")
;= (+ 1 2 8)
Whitespace and Commas:
whitespace is sufficient to separate values and forms provided to the reader. commas are considered whitespace by the reader.
(= [1 2 3] [1, 2, 3])
;= true
most common usage for comma is when more than one pair of values appears per line:
(create-user {:name new-username, :email email})
Collection Literals:
'(a b :name 12.5) ;; list
['a 'b :name 12.5] ;; vector
{:name "Chas" :age 31} ;; map
#{1 2 3} ;; set
since lists are used to denote calls in Clojure, you need to quote '
the list literal
Namespaces
All Clojure code is defined and evaluated within a namespace.
Vars are defined in Clojure using the def
special form, which only ever acts within the current namespace.
the current namespace is always bound to *ns*
Code Blocks: do
do
evaluates all of the expressions provided to it in order and yields the last expression's value as its value
(do
(println "hi")
(apply * [4 5 6]))
; hi
;= 120
Local Bindings: let
let
defines locals.
Creating Functions: fn
(fn [x]
(+ 10 x))
(defn adder [x]
(+ 1 x))
Function Literals
(fn [x y] (Math/pow x y))
#(Math/pow %1 %2)
no implicit do form
#(do (println (str %1 \^ %2))
(Math/pow %1 %2))
function literals cannot be nested.
Conditions: if
Clojure conditionals determine logical truth to be anything other than nil
or false
.
if a conditional expression is logically false, and no else
expression is provided, the result of an if
expression is nil
Looping: loop and recur
appropriate use of recur: recur
is a very low-level looping and recursion operation that is usually not necessary:
- use
doseq
anddotimes
- when "iterating" over a collection or sequence, use
map
,reduce
,for
and so on.
Chapter 2, Functional Programming
(ok, i gave up here, this book is not for new learners)
(update: able to finish it on the 3rd trial, it's difficult but worth the time.)