(if (cond)
(progn
(preamble)
(let ((a 5))
(do-stuff a)))
(do-stuff 6))
Above would be unusual. I'd only write code that way if preamble was dependent on some previous binding of a. Note that let subsumes progn in most cases.
I'd write it like this: (if (cond)
(let ((a 5))
(preamble)
(do-stuff a))
(do-stuff 6))No. Explicit scoping is a huge plus.
> This is why languages die, when the old guard refuses to see that there are better ways of doing things than what they are used to.
Don't mistake this for not being able to see beyond your nose.
edit:
Let me also add this: you cannot get away from scoping. If you create intermediate values in your language of choice you better understand the implicit (and sometimes very complicated) scoping rules of that language. All that Common Lisp does is it makes this scoping explicit.
In my opinion, it is a giant, flaming misfeature.
> Which is to say, almost every language apart from Common Lisp
Algol; Ada; Modula 1, 2, and 3; Oberon; Eiffel; ...
Racket also allows it and encourages it in the style guide, using the "less nesting" (and thus shallower indentation ) rationale you bring up.
(if (cond)
(begin
(preamble)
(define a 5)
(do-stuff a))
(do-stuff 6))
This is not defining a local variable in Scheme. The various Scheme standards also require that DEFINE appears at the top of a body.> Common Lisp wants you to declare every set of intermediate variables in a nested scope
Lisp programmers edit code by list operations. For example, I can set the cursor between (preamble) and the (let ...) form. control-meta-t transposes the lists. The let form is moved upwards and the enclosed body is moved, too. Try that in the C code. Code transformations are vastly easier with explicit scopes.
JavaScript has introduced a LET for a reason: block scope is clearer than VAR (function / global scope).
(defun foo (a)
(if a
(var b a))
(print b)) ; what's the value here?
In Common Lisp above would not be valid.I would need to write:
(defun foo (a)
(let (b)
(if a
(setf b a))
(print b)))
I can then immediately see that each B is inside a LET scope, which defines it. It's just by simply moving upwards in the expression tree. I would not need to search the whole expression tree. Also languages may do different things with definitions inside conditional expressions. (if (cond)
(let ((a 5))
(preamble)
(do-stuff a))
(do-stuff 6))
You can work in side effects into the expressions of a let or let*: (let* ((x (progn (widget-lock w)
(widget-x w)))
(y ...))
(...)
(widget-unlock w))
If lock-widget returns the widget, it can look like this: (let* ((w (widget-lock w))
(x (widget-x x))
(y ...))
...
(widget-unlock w))
> And if it's executed conditionally, what does that mean? The var is declared on one branch but not the other?
Here is some C code:
And some Scheme code: So why can't Common Lisp have the equivalent? Instead, CL forces you to do this: Do you see the problem? Common Lisp wants you to declare every set of intermediate variables in a nested scope, leading to super-deep nesting unless you start breaking you function into small pieces for no reason (which then hurts readability).This is why languages die, when the old guard refuses to see that there are better ways of doing things than what they are used to.