5 ms·
Why is this a problem for Common Lisp, and not for the dozens of Scheme implementations that allow you to (define ...) anywhere? (never mind what the RnRS stand
by HexDecOctBin 3y ago
Why is this a problem for Common Lisp, and not for the dozens of Scheme implementations that allow you to (define ...) anywhere? (never mind what the RnRS standard says)
> 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:
if (cond()) {
preamble();
int a = 5;
do_stuff(a);
} else {
do_stuff(6);
}
And some Scheme code:
(if (cond)
(begin
(preamble)
(define a 5)
(do-stuff a))
(do-stuff 6))
So why can't Common Lisp have the equivalent?
(if (cond)
(progn
(preamble)
(var a 5)
(do-stuff a))
(do-stuff 6))
Instead, CL forces you to do this:
(if (cond)
(progn
(preamble)
(let ((a 5))
(do-stuff a)))
(do-stuff 6))
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.
- deleted 3y ago[deleted]
- medo-bear 3y ago> Do you see the problem? 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.
- HexDecOctBin 3y agoSo, every other language that doesn't create a new nested scope for every consecutive group of intermediate variables is doing it wrong? Which is to say, almost every language apart from Common Lisp. Does that sound reasonable to you?
- kragen 3y agowhile i'm not sure lisp's approach is better in this case, i do think it's fairly common that 'almost every language apart from ... lisp' 'is doing it wrong', so i don't think that would be an unreasonable position to hold on those grounds ;)
- a-french-anon 3y agoYou're talking about a special operator, a language primitive. In that view, let is perfectly fine and clear. Macros are what you want if syntactic sugar is needed.
- Karrot_Kream 3y agoYou've rediscovered another essential portion of Lisp: the smug lisp weenie.
- medo-bear 3y agoI'd instead point out that an appeal to majority is a basic fallacy. 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.
- kazinator 3y ago> So, every other language that doesn't create a new nested scope for every consecutive group of intermediate variables is doing it wrong? 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; ...
- pfdietz 3y agoNesting is a general problem beyond let, so it deserves a general solution. A solution in Lisp is the nest macro. https://fare.livejournal.com/189741.html https://fare.livejournal.com/189741.html
- dreamcompiler 3y ago> Instead, CL forces you to do this: (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))
- deleted 3y ago[deleted]
- shawn_w 3y agoScheme only allows internal defines at the start of a block, where they're all combined into a letrec (or at least treated the same way). A few do allow define anywhere; SRFI-245, which gives a formal description of the semantics, lists 5 implementations that do, including some popular ones, but it's hardly dozens. Racket also allows it and encourages it in the style guide, using the "less nesting" (and thus shallower indentation ) rationale you bring up. https://srfi.schemers.org/srfi-245/srfi-245.html https://srfi.schemers.org/srfi-245/srfi-245.html
- lispm 3y ago> And some Scheme code: (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.
- kazinator 3y agoSince (preamble) cannot possibly perform a side effect which affects the initializing expression 5, it can be moved: (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))