News for hackers. Stuff that matters.
Re:Differences with other Lisps by rawoke083600 on Wednesday November 03, 2021 @02:48AM (#29091751)
Maybe a more seasoned LISP-`aterian` can answer me ?

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`

    (defn myfunc []
       (let [some-var (:some-key some-state)])
       (.log js/console some-var)
    )
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.

Re:Differences with other Lisps by ltultraweight on Wednesday November 03, 2021 @03:05AM (#29091861)
As someone that was a hardcore Lisper (you could find me arguing that Scheme is not a Lisp, etc.) before adopting Clojure: I thought muddying the language with a new kind of syntax was terrible.

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.)

Re:Differences with other Lisps by rawoke083600 on Wednesday November 03, 2021 @03:08AM (#29091874)
>A pragmatic Lisp..

As a newbie(with zero social-capital) I couldn't agree more !

Re:Differences with other Lisps by Guthur on Wednesday November 03, 2021 @06:26AM (#29092844)
Well I mean CL is absolutely littered with pragmatism it came out of a standard committee after all. In many ways I'd argue clojure is far more opinionated, it's just an opinion that might more closely align with modern sensibilities.
Re:Differences with other Lisps by Jach on Wednesday November 03, 2021 @04:26AM (#29092234)
Using [] for syntax like Clojure does is kind of weird and not too consistent IMO. As for data structures, CL has vectors (and more complex arrays), with #() as a literal syntax or my preference just the function (vector ...). But beyond that, CL is just not nearly as opinionated as other languages. You are free to define a function named [] as a wrapper around aref, if you wanted to write things like ([] array index), or you can define a reader macro that uses [ or ] and use them to implement whatever syntactic sugar you desire. That might be as an alternative to #() for vector literals and similar to Clojure, or something that mixes in commas and is more similar to JSON (https://gist.github.com/chaitanyagupta/9324402), or it could be to use them as syntax where you'd usually use ()s, or as some lambda shorthand e.g. [* _ 10] as shorthand for (lambda (_) (* _ 10)), or as sugar to wrap a special text type (e.g. [hello world] produces an object that wraps the string "hello world"), or to have List Comprehensions e.g. this paper from 1991: https://3e8.org/pub/scheme/doc/lisp-pointers/v4i2/p16-lapalm... whose code (that still works) in figure 2 + the bits right after to the set-macro-character calls lets you write things like [x (x <- xs) (oddp x)] to filter out odds, or [(* x x) (x <- xs)] to get squares, and you can do things with multiple lists. Or something else. Ultimately, it comes back to CL letting you do what you want.
Re:Differences with other Lisps by nlitened on Wednesday November 03, 2021 @05:17AM (#29092474)
> Using [] for syntax like Clojure does is kind of weird and not too consistent IMO

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.

Re:Differences with other Lisps by Jach on Wednesday November 03, 2021 @07:30AM (#29093249)
Neat, I don't remember hearing about the "command" justification before. I suppose this is a rationale behind e.g. the 'ns macro separating its command-keywords that map to functions with parens? Still, I always thought the full syntax of 'ns and the individual things inside of it was a bit wild. Thank goodness for Slamhound...

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.

Re:Differences with other Lisps by nlitened on Wednesday November 03, 2021 @08:07AM (#29093502)
Oh yeah, you're right, multi-arity fns are also a big exception to the empirical "rule" I mentioned. And yes, (ns) macro looks like a historical mess indeed.

> 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.

Re:Differences with other Lisps by Jach on Wednesday November 03, 2021 @10:14AM (#29094865)
Right, as data literals my program is going to use they're great, I fell in love with Python a long time ago in part because of its convenient data syntax. Clojure's values-are-values philosophy is also amazing. How much value are they though as core syntax forms? Take keyword parameters for instance. In Clojure you pass to defn something like [a & {:keys [x y]}]. In Common Lisp, that would be (a &key x y). If you want to add default values, in Clojure you'd change to [a & {:keys [x y] :or {x 1, y 2}}]. In Common Lisp, that would be (a &key (x 1) (y 2)). They're both data structures, they both require interpretation by the human and the defn/defun implementation, they're both not eagerly evaluated like other uses of data literals would be (neither the sequences nor the symbols need to be quoted). Apart from Clojure strangely bucking tradition with everything by making the top structure a vector instead of a list, and a quibble of repeating yourself for default mapping, I don't really think one is definitively better than the other, but that also means I don't think Clojure improved on anything with that choice.
Re:Differences with other Lisps by lispm on Wednesday November 03, 2021 @03:08PM (#29098967)
In Common Lisp the arglist is not a runtime datastructure, unless one uses something like APPLY. This means that the usual implementation (compiler/interpreter) will check the declared keyword arguments: is it duplicate, is it used, ...? For calls it can be checked if the keyword is known. That's usually all done by default.
Re:Differences with other Lisps by patrec on Wednesday November 03, 2021 @03:22AM (#29091942)
The reality is that just about every single lisper will insist how they don't see the parenthesis and they are not a problem, and just about every single lisp has some ugly ad hoc hacks to reduce their clutter. They just differ somewhat between lisps.

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]

Re:Differences with other Lisps by bjoli on Wednesday November 03, 2021 @03:46AM (#29092052)
Only R6RS made () and [] interchangeable. That was hugely controversial, with opponents (myself included) thinking it was a waste of 1/3 of the matching pair symbols.

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

Re:Differences with other Lisps by deckard1 on Wednesday November 03, 2021 @04:29AM (#29092247)
Scheme reports lag, though. They usually codify what existing Scheme compiler/interpreters are already doing. I know I've seen plenty of Scheme code in the wild using square brackets interchangeably long before R6RS came out. You can even see the rationale behind this in the Scheme FAQ on Scheme Wiki, which seems to not have been updated since R5RS was current [1]

[1] http://community.schemewiki.org/?scheme-faq-language#H-yoyi8...

Re:Differences with other Lisps by lispm on Wednesday November 03, 2021 @07:42AM (#29093339)
The use of square brackets in Lisp and Scheme is much older. IIRC there were Scheme books which used them much earlier.

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.

Re:Differences with other Lisps by patrec on Wednesday November 03, 2021 @04:26AM (#29092231)
I fully agree (and thanks for the idiomatic let bracket ordering correction). But the fact that terribly hacks like this are nonetheless adopted shows how much of a pain the paren clutter is and that claims to the contrary are pure cope.
Re:Differences with other Lisps by bjoli on Wednesday November 03, 2021 @07:21PM (#29101812)
It is not a terrible hack. It is a useful thing to do if you dont have things like paren highlighting or paredit.

I am firmly in the "paren clutter doesn't exist" camp.

Re:Differences with other Lisps by mpfundstein on Wednesday November 03, 2021 @03:33PM (#29099247)
i maintain two large CL code basis. there is no pain because of parens. really not...
Re:Differences with other Lisps by Zababa on Wednesday November 03, 2021 @06:14AM (#29092762)
> a waste of 1/3 of the matching pair symbols

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?

Re:Differences with other Lisps by bjoli on Wednesday November 03, 2021 @07:18PM (#29101790)
There are functions starting with < and > so that's no good. And / and \ are already used elsewhere.
Re:Differences with other Lisps by adrian_b on Wednesday November 03, 2021 @07:28AM (#29093234)
There have been LISP dialects where a single closing square bracket was used to close all the remaining open round parentheses.
Re:Differences with other Lisps by medstrom on Wednesday November 03, 2021 @09:53AM (#29094550)
Sounds useful for an interactive shell. Perhaps even Fish shell could adopt it.
Re:Differences with other Lisps by krubar on Wednesday November 03, 2021 @06:00AM (#29092689)
IIRC, creator of Clojure, Rich Hickey once said that people in general handle things better where there is some variety in symbols used for different things, when you "scan" the code with your eyes. Too much variety -> you end up with explosion of symbols and operators, not good; too little variety -> hard to distinguish things.
Re:Differences with other Lisps by linguae on Wednesday November 03, 2021 @03:01AM (#29091834)
Racket also optionally supports a square bracket syntax for cons clauses.

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.

Re:Differences with other Lisps by tralarpa on Wednesday November 03, 2021 @03:55AM (#29092086)
> It is also used in let/let*/letrec bindings in Racket

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.

Re:Differences with other Lisps by rawoke083600 on Wednesday November 03, 2021 @03:06AM (#29091865)
Interesting ! Thank you for the examples. Yes Racket is also on my someday-soon-list !
Re:Differences with other Lisps by Munksgaard on Wednesday November 03, 2021 @03:26AM (#29091960)
In Clojure, `[1 2 3]` is a vector, which is a distinct data type from a list, which looks like `(1 2 3)`. A vector in Common Lisp is written using `#(1 2 3)`.

Using vectors for function arguments is unique to Clojure, and I'm not entirely sure why they chose to do it that way.

Re:Differences with other Lisps by iLemming on Wednesday November 03, 2021 @09:38AM (#29094367)
> 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

Re:Differences with other Lisps by Kototama on Wednesday November 03, 2021 @08:31AM (#29093698)
Previous lisps had only the list as a datastructure with a special syntax and the program was only represented as lists.

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".