Some of Lisp appreciation is like appreciating the Pixies: it's hard to see what's so amazing about it when so many of its good points have been expropriated by later projects. It's easy to see what's so powerful about Lisp when your only comparison is C, but it's not 1998 anymore, and most new code is written in high-level garbage collected languages.
The rest of Lisp you appreciate by learning to appreciate real macros.
I've read a few books about lisp tininess, beauty etc. I don't know it was late 70s research bias or not. But it seems that no matter how many lisp genes are picked up by other language. Its symbolic/recursive nature can't be matched, and everything else is secondary.
It's the syntax. The elementary nature of s-expressions makes it fairly trivial to mangle them freely, and define all kinds of clever tree structures and so on, all in an easy and consistent way. It also means you never have to remember much symbolic weirdness, you just have to remember (operator atom atoms ...), and that's it.
I think only the functional languages like Haskell and F# come close to the same expressiveness, just from very different angles; auto-currying and lazy evaluation save a lot of boilerplate, and the former is tricky to do in Lisp for precisely one of the reasons it's so flexible (easy variable arguments).
It's a little more than syntax to me, even though it's part of it. Maybe a ~rapprochement between syntax semantics evaluation of recursive domains/categories. The fact that the construction of data through sexps belongs to that same philosophy as valuation between any domains. That the same recursive idea permeates through the whole system.
Lisps do a fantastic job following the principle of least astonishment ( https://en.wikipedia.org/wiki/Principle_of_least_astonishmen... ). I work with Clojure professionally and I find there's a huge advantage to Lisp syntax compared to other languages that I've used. The syntax is very minimal and consistent while also extremely expressive.
Practically all other languages tend provide expressiveness by having tons of syntax sugar. This introduces a lot of special cases, quirks, and rules that you have to keep in your head to work with the language. It's constant mental overhead that distracts me from the problem I'm working on.
A language like Clojure just gets out of your way and lets you focus on your work without having to worry about its quirks all the time. The code tends to work as expected and does what it looks like it's doing on the first try.
The other big advantage is that s-expressions make it much easier to write structural editors like paredit. With Clojure I'm always thinking in terms of manipulating expressions as opposed to lines of code. I think of taking this block of logic and putting it in there and so on.
Finally, the syntax provides additional visual information about the flow of the program. You can tell what parts of code are interacting with one another simply by looking at the nesting. This makes the code much more easily scannable than other languages.
The syntax for function definitions becomes the following:
(fn name? [params* ] condition-map? exprs*)
(fn name? ([params* ] condition-map? exprs*)+)
Read the word syntax? See the EBNF like syntax descriptions with star, question mark and plus syntax operators? See parentheses structure with [] and () in the second example?
star -> zero or more expressions
plus -> one or more expressions
question mark -> one optional expression
That's syntax. It describes the structure of s-expressions which form valid Clojure code.
Not all Clojure s-expressions are valid Clojure code. There are lots of syntactic restrictions. See the syntax for 'fn' above for an example.
> your own private definition notwithstanding.
You may want to take over the Clojure.org site to follow your own, private, definition of Clojure syntax. Personally I prefer the usual definition of Clojure syntax, as for example shown on Clojure.org .
Claiming that Clojure only has s-expressions as syntax is thus wrong and misleading.
Of course Common Liep has s-expressions. Everybody knows that.
But s-expression syntax is not the complete syntax of Lisp programs. Lisp programs are written using s-expressions. But not every s-expression is a valid Lisp program. There is syntax for lambda expressions, various special forms and syntax implemented by macros.
S-expression syntax OTOH is only the syntax for, wait, s-expressions.
A Lisp compiler will check the syntax of Lisp code at compile time:
* (defun foo () (let a ((b 10)) 3))
; in: DEFUN FOO
; (LET A
; ((B 10))
; 3)
;
; caught ERROR:
; Malformed LET bindings: A.
Here the syntax for LET is violated.
The syntax for LET is defined as:
let ({var | (var [init-form])}*) declaration* form* => result*
Thus the compiler expects a list of bindings and not a symbol, as in my example above.
Every Lisp programmer will need to learn this syntax. Not just ATOM plus (S-EXPRESSION*).
I'm not really sure what your point here. These are implementation details of how CL works. Has nothing at all to do with what s-expressions are in principle.
I'm not sure why it's so difficult to understand for you.
S-expressions give us a textual syntax for data. But not for Lisp. Lisp syntax is built on top of s-expressions and has to be learned. That's true for Scheme, too. See chapter 7.1 of the Scheme R7RS specification: "Formal syntax". That's not s-expressions.
> I'm not really sure what your point here.
That seems to be a common theme for you. I'll help you out: Your original points were this:
> Clojure syntax is s-expressions, your own private definition notwithstanding.
Which I showed you by pointing to Clojure.org to be wrong. Clojure syntax is more than s-expressions.
> The syntax is very minimal and consistent while also extremely expressive.
This also is wrong, since the syntax consists of all kinds special forms and macros, too. The user can even introduce lots of new syntax by implementing it as macros. Every library with macros is likely introducing new syntax and semantics.
Isn't it obvious? Reader macros are in the first place there to implement and extend the s-expression reader. It implements parsing of the data structures: conses, lists, arrays, vectors, strings, symbols, various number types, bit vectors, ...
The rest of Lisp you appreciate by learning to appreciate real macros.