We often say that Clojure as a LISP dialect is homoiconic: The syntax of the code is the same as the syntax of the data.
I am asking myself what parts of Clojure should we consider as parts of the Clojure Syntax?
I am not sure the question is well formulated. Anyway, the purpose of this question is not academic. I is to help newcomers to learn Clojure effectively.
The way I see it, the whole Clojure language could be more or less layered in the following components:
S expressions as in the original LISP
EDN to support collections literals, regular expressions ā¦
Special forms: if, def, let, do ā¦
Macros defined in clojure.core: defn, defmacro, when, if-not, and, cond ā¦
Functions defined in clojure.core: map, filter, reduce ā¦
Functions defined in clojure other namespaces: clojure.set, clojure.walk ā¦
What is the limit between the syntax and the semantics?
Is there such a limit?
Is it important to draw such a limit?
In my mind, the core Clojure syntax for this use should be defined by drawing parallels from how other languages commonly do operations such as code structure, function creating, conditionals, loops and so on, as well as data structures and their common operations.
Itās true that there is no syntax for this, but in my mind (esp. as a non-native English speaker), some common Clojure functions such as assoc and conjare syntax.
Intuitively, I would actually answer no to the last 2 questions, because as application programmers there is no limit to the layers of compilation of interpretation we add in our programs - and we especially do this in Clojure, what with data-based APIs and macros.
So the only distinction that doesnāt feel to arbitrary to me is that between text and data - which means thereās very little to Clojureās syntax.
Maybe a more constructive suggestion: donāt teach beginners about syntax, teach them the notions of compilation and interpretation !
Syntax is data. Semantics is functionality. Languages without āfirst classā notions of actions (in the @ericnormandāian sense) and syntactical transformations over functionality, will have a harder time bending the functionality of their data to their will.
Eight bits is a byte. Both the eight bits and the byte are syntactical representations of the same data. Between them is a semantic transformation. Each layer of the computational stack are transformations from one syntax into another, usually over the same data.
So, IMO, you canāt say āthis part of Clojure is syntax and this part is semantics.ā Itās thousands of layers of syntax, each producing different layers of semantics. In some languages, where syntax isnāt a first class thing that can be manipulated, the syntax/semantic can seem more important. In Clojure, rather, what is semantic and what is syntax depends entirely on whether youāre on the bottom side or the top side of a particular abstraction.
This is a really great question. Iām not sure the distinction between syntax and semantics is the distinction you are trying to find, though. For instance, the fact that let-bound variables are immutable is technically part of the semantics, while the fact that the bindings are wrapped in a single vector (not a list of lists as in other lisps) is syntax.
The nice thing in Clojure is that we have two data points. Thereās the JVM Clojure and thereās JavaScript Clojure. In theory, there can be more. What needs to be preserved when porting it to a new host in order for it to still be Clojure? In a conversation with David Nolen a few years ago, he gave a short list of things that made Clojure Clojure. I donāt remember them exactly, but hereās my best try at reproduction in the same spirit:
The particular surface syntax
Functions
Immutable local variables
Immutable data structures (with their same semantics)
The core library
Atoms
Protocols for type-based dispatch
The protocols defined for the basic access patterns of the data structures
Clojure just adds four persistent collections and some core functions to the JVM, and expresses the code with four persistent collections. It has no syntax. Forcing various distinctions does not help to understand Clojure.
;Conventional thinking, chaotic logic.
(if (and (> x1 x2)
(or (< x3 x4) (not= x5 x6))
(keyword? x7))
:t
:f)
;Unrestricted expression, just read in order.
;Closer to the order in which the machine is executed.
(-> (< x3 x4)
(or , (not= x5 x6))
(and , (> x1 x2))
(and , (keyword? x7))
(if , :t :f))
According to Taoism, water flow is the perfect substance. The water flow is always able to assume any shape as needed, progressing in sequence, reaching the end point. The pure function pipeline data flow is like a water flow, almost close to the Tao.Think PurefunctionPipeline&Dataflow First
I think when talking about syntax or teaching others about Clojure syntax, we donāt need to care about all built-in functions, e.g. clojure.walk/post-walk.
But itās very necessary to know about all syntax sugars, these are very common when reading Clojure code. #"", #_, @, ^, ~@, all these stuff will make beginner very confused.
So, Syntax, there must be a list of all you need to know, if you do, you donāt need go back to tutorials or books to look up something.
text: represents information as a raw sequence of characters, practical for humans
data: represents information as data structures, practical for programs
So in the case of compilation, source code is text whereas CSTs / ASTs are data - the advantage of Lisps being that they make the translation from one to the other evident. In this line of thought, I would describe syntax as the ātextualā rules for writing programs (what is a valid textual notation for data) - and semantics as the rules for interpreting that data.
Not necessarily important for Clojure beginners - but then Iām not sure the distinction between syntax and semantics is important for Clojure beginners either. It depends on where you draw the line of beginnerness. I do feel it is important, though, for the learning programmer, because a ton of what we do boils down to crafting compilers and interpreters. SICP comes to mind as a better introduction than anything I could articulate here.
The article shows how other more complicated forms are desugared. At the bottom of the article, thereās the question: āHow far can we go?ā. The answer: If we wanted to, we could desugar all the way to the simple lambda calculus.
In case someone is interested in the core language + syntax sugar approach to define languages, I first read about it in PLAI, a book about writing interpreters (using racket).