6 ms·
The idea is that the WITH variables are local to the LOOP construct. If one leaves the LOOP, the variables are gone. With an outside LET this would not be the c
by lispm 4y ago
The idea is that the WITH variables are local to the LOOP construct. If one leaves the LOOP, the variables are gone. With an outside LET this would not be the case. The LOOP thus gives scope for all lexical variables inside of it.
Additionally the LOOP sees the WITH definition in standard Common Lisp. It does not see any outside LET bindings, unless the Common Lisp would provide a feature to ask the current environment. This allows the LOOP to, for example, see a type declaration and provide a default value for the common cases.
(let (a) ; a is initialized to NIL
(declare (type integer a))
....
(loop repeat 10 do (incf a))
...
a)
vs.
(loop with a fixnum ; a is initialized to 0
repeat 10 do (incf a)
finally (return a))
It provides a compact notation with ONE scoping construct, the LOOP macro.
Similar for the LOOP name. In the same way you could argue that this is just a named BLOCK around the LOOP construct. But, again, the name inside the LOOP makes it clear that the this is the name of this LOOP construct.
Btw., the ITERATE macro works in a similar way.
- lisper 4y ago> The idea is that the WITH variables are local to the LOOP construct. If one leaves the LOOP, the variables are gone. With an outside LET this would not be the case. Of course it would. This: (loop with a = ...) is exactly equivalent to: (let ((a ...)) (loop ...)) and so you can e.g. capture with-bindings in closures: (funcall (loop with a = 123 return (lambda () a))) ==> 123 > Additionally the LOOP sees the WITH definition in standard Common Lisp. It does not see any outside LET bindings, unless the Common Lisp would provide a feature to ask the current environment. This allows the LOOP to for example see a type declaration and provide a default value for the common cases. I think it is highly questionable whether providing implicit typed default values is actually a useful feature, but assuming for the sake of argument that it is, why not just make a general binding construct that does this? If it's useful, it should be useful (and usable) everywhere, not just inside a loop. > you could argue that this is just a named BLOCK around the LOOP construct Indeed you could. :-) > the name inside the LOOP makes it clear that the this is the name of this LOOP construct. Yes, but why is that useful? How is (loop named foo ...) any better than (block foo (loop ...)) ? And if it is better, then why not (progn named foo ...) ? (tagbody named foo ...) ?
- lispm 4y agoAs I said LOOP is designed such that names, local variables, local iteration variables are all defined INSIDE the LOOP, such that LOOP can process them and that they are all using the LOOP syntax. (loop with a = ...) maybe equivalent to (let ((a ...)) (loop ...)) but (let ((a ...)) ... (loop ...) ...) is not equivalent to (loop with a = ...) The difference is that in the latter it is not clear to the Lisp developer that A is meant as a LOOP local variable. That's a design choice, not bad design. Historically LOOP was named FOR and was only a tiny part of CLISP (Conversational Lisp) in Interlisp. There CLISP had a different syntax than normal Lisp, including iteration, binding, infix operations, etc. The idea of such a LOOP macro was then brought to Maclisp, ZetaLisp and, eventually, ANSI Common Lisp.
- lisper 4y ago> The difference is that in the latter it is not clear to the Lisp developer that A is meant as a LOOP local variable. That's because in the latter example, A is NOT a loop-local variable. If you write: (let ((a ...)) (some-form)) then it is clear that a is local to (some-form) regardless of what (some-form) actually is. If you write: (let ((a ...)) (some-form) (some-other-form)) then obviously it is not clear whether a is local to (some-form) or (some-other-form) or both. But so what? If it matters that this be clear, just don't put multiple forms inside the body of your LET, and then it is clear. You don't need to design a whole new language to solve this non-existent problem. > That's a design choice, not bad design. It introduces a lot of additional complexity, including a syntax that is radically different from anything else in the language, and confers very little benefit. If that is not diagnostic of a bad design I can't imagine what would be.
- lispm 4y ago> You don't need to design a whole new language to solve this non-existent problem. The designers thought that a whole new language would be useful. One may disagree with it. I would have preferred something like the ITERATE macro, but it has exactly the same idea of ENCLOSING all of these LOOP clauses, but with more parentheses. (iterate (with a = 1) (incf a) (when (> a 10) (return a))) is (loop with a = 1 do (incf a) when (> a 10) return a) If one cares about radically different syntax then you wouldn't use LOOP at all. (loop for i in l do (print i)) is equivalent to (dolist (i l) (print i)) which is equivalent to something with a LET/TAGBODY... construct. Why not just use the latter? Maybe its design choices made it practical enough?