I don't suggest that this is what primarily drove the lack of adoption of other kind of braces but I, as a follower of the church of Lisp, had an immense dislike of the lack of purity.
In the end vector and map "syntax" is just the reader being more ergonomic in Clojure than in Common Lisp. (See https://clojure.org/reference/reader for details)
After I also got over my aversion of the Java ecosystem and just accepted that the jvm can be great without thinking Java is great, I'm all in on Clojure for the last few years. I think the language is fantastic and due to the huge amount of interop I can use my favorite language in settings that would be difficult otherwise. I've used Clojure on the web as ClojureScript, as backend as Clojure, on a ESP32 and reMarkable as ClojureScript and since relatively recent I can even accomplish scripting tasks with very good start-up times using babashka.
I'm a super happy camper and I've found my language for life. A pragmatic Lisp. (duck because stones are incoming.)
As a newbie(with zero social-capital) I couldn't agree more !
Actually, it’s very consistent. Round parentheses are used for syntax where the first element is a “command” that will be evaluated, and square brackets are used for list of things where the first element is not “special”.
For example, in `(let [x 0 y 1] (conj [2 y] x))`
- `let` and `conj` are commands that will be evaluated, and the rest of the syntax list are arguments to that command
- `x` and `2` are not commands to be evaluated, they are just members of the syntax list with no special evaluations.
The only inconsistency with this rule that comes to my mind is `case` with multiple matches, but outside of that it’s pretty consistent, and I started to like it after it “clicked” in my head.
Like, take (import [package thing-in-package other-thing-in-package]) -- the first element is "special" in how the rest are interpreted. 'gen-class takes :methods as [[method-name [arg-types] return-type]] while 'letfn separates the inner functions with ()s. Multi-arity functions use () as the separator, too. But why not []? Is the [foo] arglist^H^H^H^Hvector in ([foo] body) in (defn blah ([] body) ([foo] body) ...) meant to be a "command"? Or the name of the local function in 'letfn a command?
Using "nothing"/implicit argument pairs like in 'case/'cond/'extend-protocol/... instead of wrapping them in a vector like 'let can feel inconsistent, but it's understandably popular even if it can make things harder to edit or easier to introduce bugs. Writing things that way is a common introduction-to-macros for CL, too, and pair-lists are often used for literal maps rather than importing something that makes a hash table. Looking at Clojure's 'case again though I guess you were mentioning its ability to match multiple values with (x y z)? Ok, but it seems to me just a consequence of porting behavior from CL, not a big deal.
It still strikes me as weird that Clojure introduced nice data literal sugar, except I can't be sure that when I see it that it's always a data literal. When I see [], it might be a literal vector with every element evaluated, or it might be embedded in a context with complex syntax and effects. Like sometimes it's just a sequence of binding forms such as in simple function arguments, sometimes there's an implicit pairwise association going on like in 'let, sometimes the first element determines how the rest are interpreted, or there might be keywords thrown in the middle that determine the rest. This problem extends to some popular libraries, too (e.g. Reagent's syntax for components always bugged me). In practice it's not a huge deal, sure, and I liked it for a while, then I got used to CL and prefer its idioms.
All this isn't to say that CL is a paragon of consistency (see kludges: https://stevelosh.com/static/images/blog/2018/07/lisp-kludge...) and when it comes to syntax you still have to deal with similar issues of interpretation (CL lambda-lists have some rich behavior, the loop macro has many features, format strings too, etc. not to mention stuff in libraries, and custom reader macros doing whatever), but I am saying that I don't think Clojure's introduction of [] and {} for core syntax on top of their roles as data literals actually made things any better.
> but I am saying that I don't think Clojure's introduction of [] and {} for core syntax on top of their roles as data literals actually made things any better
I think of it as embedded JSON so that it's easier to type data structures, and with Clojure you end up with _a lot_ of data structures to type, since Clojure strongly encourages data-oriented programming.
Disclaimer: though, Clojure is my first and only lisp I've used professionally, so I am for sure missing (by not knowing better) many goodnesses that Common Lisp brings to the table.
Scheme (and descendants like racket) went so far as to make [] and () interchangeable. So you will in fact see brackets in some scheme most e.g. (let ([a 1] [b 2]) ...) is a common idiom, although old-school people stick to ().
Common lisp has stuff like loop, () aliased to nil (different connotations), and a verbose way to call passed or returned functions; it shares its infix pair syntax (head . tail) with scheme. Scheme, unlike common lisp has a pretty complex (hah) infix-syntax for complex number literals; both write rationals infix.
And have a look at the Lisp 1.5 manual some time :)
As far as such hacks go, this use of vectors in clojure is actually quite nice. Whereas the fact that clojure, unlike other lisps, uses "invisible" pairing brackets in binding constructs or "visible" whitespace in the form of commas for associative containers is a big pain in the ass for editing and formatting code.
[edit: fixed idiomatic let bracket/paren split, as pointed out by version_five]
And the suggested use was not quite the syntax you showed: http://www.r6rs.org/final/html/r6rs-app/r6rs-app-Z-H-5.html
[1] http://community.schemewiki.org/?scheme-faq-language#H-yoyi8...
Interlisp also for example uses square brackets. The left square bracket is equivalent to a round bracket and the right square bracket closes all open levels of brackets.
I am firmly in the "paren clutter doesn't exist" camp.
I count 5, (), [], {}, <> and /\ though the last one is a bit of a stretch (but it's reversible: \/). Am I missing something? Did you mean "1/3 of the matching pair symbols left" as () is already used? Or is <> not usable?
For example, in traditional Scheme one would write:
; this is in R5RS Scheme
(define (length elems)
(cond ((null? elems) 0)
(else (+ 1 (length (cdr elems))))))
But in Racket one would write: ; this is in Racket
(define (length elems)
(cond [(empty? elems) 0]
[else (+ 1 (length (rest elems)))]))
It is also used in let/let*/letrec bindings in Racket; see https://docs.racket-lang.org/guide/syntax-overview.html#%28p....I don't know the origins of when brackets started appearing in Lisps. I don't remember seeing brackets in the LISP 1.5 Programmers Manual when using S-expressions (though brackets are a core part of M-expression syntax, which LISP 1.5 still supported), and I haven't encountered brackets in Common Lisp.
I suppose this is just a personal choice of the author of the manual, right? Please correct me if I am wrong but I thought Racket doesn't care whether you use brackets, parentheses, or even curly brackets.
Using vectors for function arguments is unique to Clojure, and I'm not entirely sure why they chose to do it that way.
Once you understand how destructing works in Clojure, it becomes obvious
There are different "schools" now. Some argue that representing the program only with lists is more uniform and simple. Rich Hickey argues that the different multiple usages of lists in "traditional" lisps, such as in Common Lisp, imposes a cognitive burden so increase the complexity.
The Janet language for example follows Clojure whereas as Racket is still a modern language but follows the "tradition".
I'm in the process of learning ClojureScript - real fun so far. I have of course also looked at other LISPs and have noticed that in CJS the use of square brackets[] for vectors, let and `function params`
I almost never see square brackets in other LISPs (yes I'm truly a beginner and haven't looked too hard), but it stiked me as odd ? The square bracket syntax really helps to differentiate and with reading the syntax. (The syntax is not bad it is just different than what most programmers are use to)Am I wrong or just stupid or is there another reason most other LISP doesn't follow this "convention"?
Again I'm super-newbie, just something that jumped out at me.