1. it violates normal syntax rules you have been practicing since you were 4 years old.
Lisp will say:
(+ (- (\* 3 5) 7) (/ radius pi))
but you are taught since 5:
(3*5 - 7 + radius/pi)
2. Humans are highly adapted to using language, and understanding language constructs such as implicit context rules. Humans reduce token counts and structure in favor of implicit rules and making common patterns shorter. Lisp makes them all explicit, which forces you to cope with way more tokens. Lexical binding was added to Common Lisp almost as an afterthought, and the way LET/LET* force you to add layers of nesting demonstrates that. Every time you assign a variable the 'modern' way, you have to indent another block of code.
A concrete example is introducing local bindings with actions in between. The thought is "calculate this, do something, then continue":
This is much closer to what is actually happening, and does not require you to understand scoping rules, but it's cumbersome and stupid.
LET* handles consecutive bindings, but here the actions must happen between them. You can use PROGN inside the initializers, or introduce dummy bindings for the actions, but either way you're restructuring a flat sequence to fit the binding syntax.
That's the implicit context I mean: the statement order combined with syntax rules of the language can supply the scope, without requiring a new enclosing expression every time you introduce a local. Lexical scope itself doesn't require this nesting to be explicit in the syntax of the language and it's not helpful for it to be.
3. So many inconsistencies.
Common Lisp uses alternating keys and values for property lists:
GETF puts the container first; GETHASH and ASSOC put the key first. ASSOC also returns the matching pair, whereas GETF and GETHASH return the value as their primary result.
4. What happens at COMPILE-FILE time is arcane and almost impossible to keep straight.
5. Common Lisp often feels designed primarily to implement Common Lisp, rather than to write useful application code. Its equality predicates are a good example: EQ, EQL, EQUAL, and EQUALP give you four fixed bundles of rules organized around Lisp's own representations.
Want arrays compared by content? EQUALP does that, but also makes string comparisons case-insensitive. Want objects compared by their slots? EQUALP does that for DEFSTRUCT instances, but not ordinary CLOS instances. Want to define what equality means for your class? None of these predicates is a generic function you can extend.
Checking something basic like "when do these two values mean the same thing?", requires a separate operation of your own. None of the equality operators do anything close to what someone might actually want, unless they happen to be implementing CL in which case they are exactly what you want.
Comparison EQ EQL EQUAL EQUALP
Lists containing (1 2) NIL NIL T T
List (1 2) versus (1.0 2.0) NIL NIL NIL T
Strings containing "Ada" NIL NIL T T
String "Ada" versus "ADA" NIL NIL NIL T
Same-type DEFSTRUCT instances, identical slots NIL NIL NIL T
Same-class CLOS instances, identical slots NIL NIL NIL NIL
No, Common Lisp was not Lexical from day one. You are not familiar with the history. It was more or less a merging of several lisp dialects each sponsored by a different lisp machine vendor. Many of those were primarily not lexically scoped, although Zetalisp did add modern lexical scoping by 1985.
Steele himself mentions the lack of lexical scoping in contrast to scheme. <a href="https://research.scheme.org/lambda-papers/lambda-papers-compiler-optimization.html" rel="nofollow">https://research.scheme.org/lambda-papers/lambda-papers-comp...
> I don't even want to waste my time to unpack.
It is typical for people who can't respond substantively to brag about how they could respond but imply their relative status makes doing so beneath them.
(local
(def user (find-user user-id))
(require-admin user)
(def report (build-report user))
(write-audit-log user report)
(def receipt (send-report report))
(record-delivery receipt))
What the hell is that supposed to be? It's not common lisp. `local` is not defined in CL, it is a keyword in Racket that does something like what you typed there, but not really the same.
Anyway I don't know what you are showing here but it's nonsense in every language I'm aware of.
<a href="https://www.lispworks.com/documentation/HyperSpec/Front/X_Mast_L.htm" rel="nofollow">https://www.lispworks.com/documentation/HyperSpec/Front/X_Ma... (no local there)
<a href="https://www.lispworks.com/documentation/HyperSpec/Front/X_Mast_D.htm" rel="nofollow">https://www.lispworks.com/documentation/HyperSpec/Front/X_Ma... (no def either)
> No, Common Lisp was not Lexical from day one.
The first edition of Common Lisp the Language defined the language as having lexical scope, with the option to create special variables with dynamic scope (so it had both). Unless you're referring to some earlier draft specification, it's hard to reconcile your assertion with the history of the language.
From page 39:
> - Variable bindings normally have lexical scope and indefinite extent.
> - Variable bindings that are declared to be special have dynamic scope (indefinite scope and dynamic extent).
OK, Common Lisp as described by the standardization process has always had lexical scoping, no argument there. Lisp, including the variants of Lisp that CL was largely derived from, did not always have lexical scoping.
So what you are saying is a lot like if I said "C++11 has ALWAYS had the auto keyword!". While I ignore the fact that C++ did not always have the auto keyword, and the repeated typing of the same type signature over and over was a major complaint about the language for over 20 years. Yes, it's true, but it's just ignoring reality and not informative to the overall point I was making, which was Lisp is clearly not designed to make lexical scoping 'nice', because lisp predates the use of lexical scope being the dominant way to do scoping. You can see that in the very conception of scheme, originally designed to be "Lisp but with lexical scoping".
Edit: corrected responding to the wrong person. For the record "Jtsummers" and "iLemming" do look quite a bit more alike than two random usernames!
ltbarcly3 · · focus · HN ↗
Lisp will say:
but you are taught since 5: 2. Humans are highly adapted to using language, and understanding language constructs such as implicit context rules. Humans reduce token counts and structure in favor of implicit rules and making common patterns shorter. Lisp makes them all explicit, which forces you to cope with way more tokens. Lexical binding was added to Common Lisp almost as an afterthought, and the way LET/LET* force you to add layers of nesting demonstrates that. Every time you assign a variable the 'modern' way, you have to indent another block of code.A concrete example is introducing local bindings with actions in between. The thought is "calculate this, do something, then continue":
In Common Lisp, a direct translation adds a level of nesting for each binding: This is much closer to what is actually happening, and does not require you to understand scoping rules, but it's cumbersome and stupid.LET* handles consecutive bindings, but here the actions must happen between them. You can use PROGN inside the initializers, or introduce dummy bindings for the actions, but either way you're restructuring a flat sequence to fit the binding syntax.
That's the implicit context I mean: the statement order combined with syntax rules of the language can supply the scope, without requiring a new enclosing expression every time you introduce a local. Lexical scope itself doesn't require this nesting to be explicit in the syntax of the language and it's not helpful for it to be.
3. So many inconsistencies.
Common Lisp uses alternating keys and values for property lists:
Association lists use a list of pairs: And LET uses two-element binding lists: Lookup conventions differ as well: GETF puts the container first; GETHASH and ASSOC put the key first. ASSOC also returns the matching pair, whereas GETF and GETHASH return the value as their primary result.4. What happens at COMPILE-FILE time is arcane and almost impossible to keep straight.
5. Common Lisp often feels designed primarily to implement Common Lisp, rather than to write useful application code. Its equality predicates are a good example: EQ, EQL, EQUAL, and EQUALP give you four fixed bundles of rules organized around Lisp's own representations.
Want arrays compared by content? EQUALP does that, but also makes string comparisons case-insensitive. Want objects compared by their slots? EQUALP does that for DEFSTRUCT instances, but not ordinary CLOS instances. Want to define what equality means for your class? None of these predicates is a generic function you can extend.
Checking something basic like "when do these two values mean the same thing?", requires a separate operation of your own. None of the equality operators do anything close to what someone might actually want, unless they happen to be implementing CL in which case they are exactly what you want.
iLemming · · focus · HN ↗
> Lexical binding was added to Common Lisp almost as an afterthought
Common Lisp was lexically scoped from day one. You're arguing about CL using Elisp's history.
And your "cumbersome and stupid" snippet is that, because, well you wrote it that way.
In modern Lisp dialect like Clojure it would look even more cleaner.Lisp is the language where you do whatever syntax is suitable for the task, yourself, in an afternoon. In Python you wait for a PEP.
ltbarcly3 · · focus · HN ↗
Steele himself mentions the lack of lexical scoping in contrast to scheme. <a href="https://research.scheme.org/lambda-papers/lambda-papers-compiler-optimization.html" rel="nofollow">https://research.scheme.org/lambda-papers/lambda-papers-comp...
> I don't even want to waste my time to unpack.
It is typical for people who can't respond substantively to brag about how they could respond but imply their relative status makes doing so beneath them.
What the hell is that supposed to be? It's not common lisp. `local` is not defined in CL, it is a keyword in Racket that does something like what you typed there, but not really the same.Anyway I don't know what you are showing here but it's nonsense in every language I'm aware of.
<a href="https://www.lispworks.com/documentation/HyperSpec/Front/X_Mast_L.htm" rel="nofollow">https://www.lispworks.com/documentation/HyperSpec/Front/X_Ma... (no local there)
<a href="https://www.lispworks.com/documentation/HyperSpec/Front/X_Mast_D.htm" rel="nofollow">https://www.lispworks.com/documentation/HyperSpec/Front/X_Ma... (no def either)
Jtsummers · · focus · HN ↗
The first edition of Common Lisp the Language defined the language as having lexical scope, with the option to create special variables with dynamic scope (so it had both). Unless you're referring to some earlier draft specification, it's hard to reconcile your assertion with the history of the language.
From page 39:
> - Variable bindings normally have lexical scope and indefinite extent.
> - Variable bindings that are declared to be special have dynamic scope (indefinite scope and dynamic extent).
ltbarcly3 · · focus · HN ↗
So what you are saying is a lot like if I said "C++11 has ALWAYS had the auto keyword!". While I ignore the fact that C++ did not always have the auto keyword, and the repeated typing of the same type signature over and over was a major complaint about the language for over 20 years. Yes, it's true, but it's just ignoring reality and not informative to the overall point I was making, which was Lisp is clearly not designed to make lexical scoping 'nice', because lisp predates the use of lexical scope being the dominant way to do scoping. You can see that in the very conception of scheme, originally designed to be "Lisp but with lexical scoping".
Edit: corrected responding to the wrong person. For the record "Jtsummers" and "iLemming" do look quite a bit more alike than two random usernames!
iLemming · · focus · HN ↗
Great. Arguing about "readability" with a dyslexic person. What could ever go wrong here?