✓ Homoiconicity: Data First, Syntax Second¶
Homoiconic syntax¶
Lisp in general has a unique property known as homoiconicity. This is when the syntax of the language is 100% data definitions. Clojure is an homoiconic language.
Homoiconcity¶
Definition
Let \(L\) be the syntactic definitions of all programming constructs of the programming language.
We can separate the definitions into two categories: programming \(L_\mathrm{prog}\) and data \(L_\mathrm{data}\).
The language is homoiconic if
Consequence¶
The consequence is:
Namely, all programs of a homoiconic language are data.
Programs can parse programs
Programs can transform programs
Programs can generate programs
Scalars¶
Numbers¶
Integers
Floats
BigDecimal
;;
;; An integer
;;
123
123
;;
;; A float
;;
3.1415
3.1415
;;
;; A bigdecimal
;;
3.1415M
3.1415M
Strings and characters¶
Strings are with double quotes. Multilines are supported.
"hello
world
in multiple lines"
"hello\nworld\nin multiple lines"
Character literals start with \...
.
\h
\h
Keywords¶
Clojure has keywords as a special type of scalar. Keyword literals look like :...
:last-name
:last-name
Data Structures¶
Lists¶
Lists are linked lists. Elements in a list can be anything including other lists. The syntax of lists is just whitespace separated list of expressions enclosed by parenthesis.
( ... )
Vectors¶
Vectors are dynamic arrays. They differ from lists mainly from a performance perspective:
List append and element access are done in \(\mathcal{O}(n)\).
Vector append and element access are done in \(\mathcal{O}(1)\).
The syntax of vectors is whitespace separated elements enclosed by square brackets.
[ ... ]
Sets¶
Sets are collections of elements with the properties:
all elements in a set must be all distinct
different ordering of elements does not change the set
The syntax of sets is:
#{ ... }
Maps¶
Maps are hash-maps containing key-value pair entries. The keys in a map must uniquely identify the entry.
The syntax of map literals is a whitespace separated of even number of expressions of key/value pairs, enclosed by {}
.
{ key1 val1 key2 val2 key3 val3 ... }
Programming Constructs¶
Clojure provides a runtime environment built on top of the Java Virtual Machine (JVM). Clojure itself extends JVM in several important ways as well (e.g. lock-free concurrency using transactional memory and reactive programming).
To perform basic data processing, computation, and runtime activities, one needs to compose programs using different programming constructs.
Some examples of programming constructs:
Function declaration and invocation
Declaration of symbols
Loops
Branches
A lot more…
Homoiconicity again¶
Clojure models its programming constructs as data. This eliminates any need for specialized syntax for programming.
By default, lists are programs (not data).
The first element of a list determines the programming construct.
Function declaration expressions¶
(fn ...)
a list of symbols for the parameters: this is done using a vector.
an expression as the body: this is the return value.
(fn [ <parameter-list> ] <body>)
(fn [x y z] (+ x y z))
#function[user/eval5374/fn--5375]
Function invocation¶
Lists are, by default, invocations.
( <function-expression> x y z ... )
(+ 1 2 3)
6
((fn [x y z] (+ x y z)) 1 2 3)
6
Top-level symbol binding¶
(def <name> <expression>)
(def add-triple (fn [x y z] (+ x y z)))
#'user/add-triple
(add-triple 1 2 3)
6
Reading Assignment