Clojure let vs Common Lisp let
Asked Answered
K

2

6

In Common Lisp, the let uses a list for a bindings, i.e:

(let ((var1 1)
      (var2 2))
  ...)

While Clojure uses a vector instead:

(let [a 1
      b 2]
  ...)

Is there any specific reason, other than readability, for Clojure to use a vector?

Keppel answered 31/3, 2015 at 9:20 Comment(2)
The LET syntax is not a specific feature of Common Lisp. Basically every Lisp (Emacs Lisp, ISLisp, Scheme, ...) supports this syntax. Clojure does not.Fibrin
Clojure let binds sequentially. Thus it's equivalent to CL and Scheme let*.Genera
A
8

You can find Rich Hickey's argument at Simple Made Easy - slide 14, about 26 minutes in:

Simple Made Easy - slide 14

Arabinose answered 31/3, 2015 at 9:56 Comment(5)
Yeah, I think the clue is in this sentence: "overloaded for calls ang grouping". Thanks!Keppel
Now square brackets are overloaded with (less) grouping and vector syntax.Fibrin
@RainerJoswig If it weren't that they bind sequentially, it might have been more logical to use a map literal {...} instead of a vector literal [...] for Clojure let and loop.Arabinose
"overloaded for calls and grouping" That's the point of Lisp. Everything is a list. "Function calls" are just evaling lists.Goon
@Goon I forbore expressing my own view above. However, here goes ... If you have several special forms for data structures, as Clojure has, it makes sense to use them to organise programs for the eye (call it readability or reducing cognitive load or what you wll). Whether you ought to have several such special forms in the first place is moot. I stand nearer to Rich than to you on this, but it's a matter of taste. For example, to me, multiple arity functions would be hard to comprehend in classic Lisp syntax.Arabinose
D
4

Rich's line on this was as follows

"Since we were talking about syntax, let’s look at classic Lisp. It seems to be the simplest of syntax, everything is a parenthesized list of symbols, numbers, and a few other things. What could be simpler? But in reality, it is not the simplest, since to achieve that uniformity, there has to be substantial overloading of the meaning of lists. They might be function calls, grouping constructs, or data literals, etc. And determining which requires using context, increasing the cognitive load when scanning code to assess its meaning. Clojure adds a couple more composite data literals to lists, and uses them for syntax. In doing so, it means that lists are almost always call-like things, and vectors are used for grouping, and maps have their own literals. Moving from one data structure to three reduces the cognitive load substantially."

One of the things he believes was overloaded in the standard syntax was access time. So vector syntax in arguments is related to the constant access time when you used them. He said:

Seems odd though as it only is valid for that one form...as soon as it is stored in a variable or passed in any way the information is 'lost'. For example...

(defn test [a]
  (nth a 0)) ;;<- what is the access time of and element of a?

I personally prefer harsh syntax changes like brackets to be reserved for when the programmer has to switch mental models e.g. for embedded languages.

;; Example showing a possible syntax for an embedded prolog.

{size [],0}
{size([H|T],N) :- size(T,N1), N is N1+1}

(size '(1 2 3 4) 'n) ;; here we are back to lisp code

Such a concept is syntactically constant. You don't 'pass around' structure at runtime. Before runtime (read/macro/compile time) is another matter though so where possible it is often better to keep things as lists.

[edit] The original source seems to have gone, but here is another record of the interview: https://gist.github.com/rduplain/c474a80d173e6ae78980b91bc92f43d1#file-code-quarterly-rich-hickey-2011-md

Derna answered 31/3, 2015 at 17:2 Comment(5)
April fools? Access time can't be overloaded. Not in a syntax, not in general. Access time can be observed or expected. Vectors in Clojure don't have constant access time. Reference names don't convey type or access time information unless you name them so that they do. In any case this is irrelevant to syntax and construction via literals. Clojure does not "embed" Prolog or any language, so the preference to preserve brackets for embedding languages is like asking for a car to have wings instead of doors. Clojures top-level function definition macro is called defn, not "defun".Headwork
I have fixed the typo and to clarify, Rich's line (I'm trying to find the source) was that the way list was used was overloaded. Not just in terms of grouping but also in access time. Because the access time of a element is a list and an element in a vector are not the same, so vectors for grouping arguments are a better choice as it reflects that (roughly) constant access time. I also didn’t say that clojure has embedded prolog, I said I prefer syntax being reserved for use of things like embedded languages. You also may have noticed the common-lisp tag on the question...Derna
..this means there are going to be answers pertaining to things you can do in common lisp such as extending the readerDerna
Vectors for grouping arguments, called binding vectors, were chosen to be vectors for aesthetic reasons. Their superior random access time characteristics don't matter: Runtime performance is not affected and compilation performance should be better for lists since accessing their first element has O(1) characteristics. Please refer to the relevant part of the compiler source code of the fn* special form here: github.com/clojure/clojure/blob/…Headwork
I know. I am trying to state that the syntactic concept is overloaded...no runtime or compile time implication, purely cognitive. I cant find the quote and I don't have time to dig through all the videos right now to find it. Nothing you have said is wrong, it just doesnt apply to what I'm trying (poorly I admit) to convey. Anyway, cheers for well reasoned comments and good links. Time for a beer, ciaoDerna

© 2022 - 2024 — McMap. All rights reserved.