{:check ["true"], :ranks ["eval" "references"]}
box
Why data first?
Clojure is a data-driven language (much as other function languages). Furthermore, like other variants of Lisp, Clojure's syntax has a property known as homoiconicity, which means that its own source code is a valid data structure expressed in its own language.
Numbers
1 ; an integer
3.14 ; a double
123M ; a BigDecimal
3.1415M ; a BigDecimal
-3.1415 ; a negative number (double)
Warning: - 3.1415
is different from -3.1415
.
Strings and characters
"hello world" ; a string
\h ; a character
Booleans
true ; boolean true
false ; boolean false
The nil value
Unlike other languages, Clojure makes heavy usage of the nil
value,
and does not lightly throw errors when encountering it.
nil
Keywords
Keywords are handy atomic user defined values.
:hello ; a keyword
:world ; another keyword
:csci-3055u ; yet-another keyword
Symbols
Symbols are not new to Clojure. They are just the variable names in other languages. What is new in Clojure is that symbols are also data.
username ; a symbol
click-counter ; a symbol
f' ; another symbol
∂f∂y ; yes this is a symbol too
*** ; a symbol as well - this is a valid variable name.
Lists are values enclosed in ( ... )
separated by whitespace.
(1 2 3)
("Hello" "world" "again" "and" "again")
(:hello :world :again :and :again)
Clojure lists are heterogeneous, and can contain any mixture of data types.
("Ready?" 3 2 1 :go!)
(1 2 (3 4 ("hello" "world")))
("Canada" (:capital "Ottawa")
(:population (36 :million))
(:continent "North America"))
Lists are linked lists:
Lists are implemented as linked lists. Prepending elements to lists is very efficient (constant time), but random access and appending to a list are slow (linear scan).
Vectors are heterogeneous dynamic arrays with fast random access:
[1 2 3]
["Ready?" 3 2 1 :go!]
["Canada" (:capital "Ottawa")
(:population (36 :million))
(:content "North America")]
Clojure is a feature rich language, especially when it comes to data description. The language provides low-cost syntax for maps and sets.
Maps are just a "list" of key-value pairs, enclosed in { ... }
:
{:code "CSCI 3050U"
:title "Programming Languages"
:instructor "Ken Pu"
}
Maps can be deeply nested with heterogeneous data structures
{:code "CSCI 3055U"
:title "Programming Languages"
:semester {:year 2020
:season :fall}
:instructor {:name "Ken Pu"
:office nil}
:quizzes [ {:date "2020-09-01" :grade 20}
{:date "2020-09-07" :grade 10}
{:date "2020-09-14" :grade 20} ]
Sets are unordered lists that do not allow duplicate values. The syntax
is #{ ... }
.
#{"hello" "world" "again"}
All Clojure data types support equality comparison. So, sets can contain data structures as well.
#{ {:code "CSCI 3055U" :title "Programming Languages"}
{:code "CSCI 2000U" :title "Scientific Data Analysis"} }
Before we delve into the deep end of Clojure programming, we need to understand the lifecycle of Clojure data expressions.
Code as data
Computation is described as Clojure data. This is known as the homoiconic property of a programming language. Thus:
box
- Clojure source code is strictly a Clojure data structure.
- When data is code, it goes through evaluation. which consists of two phases: reader and execution.
+-------+ reader +-------------+ execution +--------+
| Data | --------> | Computation | -----------> | Result |
+-------+ +-------------+ +--------+
Data as code
Only lists and symbols are evaluated as computation. All other types of values are considered as data.
All forms of computational constructs are expressed as lists. While Clojure's syntax is simple and small, through declaration using lists, Clojure offeres a rich set of computational models.
functional programming, lazy evaluation, concurrency, polymorphism.
Data as data
It's possible to suppress evaluation of lists using the quoted form.
'(+ 1 2)
; => (clojure.core/+ 1 2)
(+ 1 2)
; => 3
'x
; => x
x
; => Unable to resolve symbol: x in this context
Quotes are recursively applied.
You just need to quote an expression and all nested lists are also quoted.
'{:count (+ 1 2)
:title (str "Hello " "World")}
; => {:count (+ 1 2) :title (str "Hello " "World")}
{:count (+ 1 2)
:title (str "Hello " "World")}
; => {:count 3 :title "Hello World")}