27 comments

[ 3.7 ms ] story [ 385 ms ] thread
Where that leads: the MIT Loop Macro.

http://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/l...

This essentially adds a FOR statement to LISP, using 1495 lines of LISP macros. Here's its manual: http://www.ai.sri.com/pkarp/loop.html

Because this is a macro, if you have to debug or step, you get to do so through the expanded code. This is usually not fun. But at least it's a memory-safe language, so it's not the hell of fixing C++ template bugs.

I can understand that debugging expanded code is not fun.

On the other hand, LOOP allows to naturally express iteration patterns that can be error-prone with other languages: you hardly produce off-by-one bugs contrary to hand-crafted loops.

See the Appendix of the Iterate manual for a comparison of looping constructs in CL: http://common-lisp.net/project/iterate/doc/Don_0027t-Loop-It....

It does not lead to the LOOP macro. The LOOP macro is a special approach for an embedded language for iteration. It's coming from on an older idea from Interlisp: Conversational LISP.

http://www.softwarepreservation.org/projects/LISP/interlisp/...

> This essentially adds a FOR statement to LISP

LOOP provides quite a bit more than a `FOR` statement: various forms of iteration and stepping, value accumulation, termination, flow control, ...

It's basically a special style of embedded DSL for it iteration. If one wants to implement such a thing, there is only a way through macros in Lisp. There are lots of iteration macros for Lisp - most have a slightly simplified grammar, though not necessarily less features (see the ITERATE macro, which has more features than LOOP plus more parentheses).

In other language this would have to be implemented in the language's internal implementation.

> Because this is a macro, ...

Similar to DO, DO*, DOTIMES, DOLIST, ... which are all macros in Common Lisp.

> if you have to debug or step, you get to do so through the expanded code.

like so much in Common Lisp and like so much in Lisp in general. Source translations are quite typical for this language family. It makes debugging quite a bit more difficult.

For an alternative to this look at SERIES, which is also implemented with macros, but looks and works completely different.

http://lispm.de/docs/cltl2/clm/node347.html#SECTION003400000...

Generally stepping is difficult in Lisp. If you would want to step through a call to a MAP function, you will step through its implementation and repeated calls to a closure argument, which might not be pretty.

SERIES is indeed quite a different approach.

A more Lisp-idiomatic approach to iteration, though not so far from LOOP, is ITERATE.

http://www.common-lisp.net/project/iterate/doc/Don_0027t-Loo...

The main thing which makes ITERATE more idiomatic in Lisp from a syntactic point of view is this: ITERATE groups clauses as sublists.

Other than that the ITERATE approach is very similar to LOOP.

    (iterate (for el in num-list)
             (when (> el 3)
               (collect el)))
vs.

    (loop for el in num-list
          when (> el 3)
            collect el)

As you can see from the above example, ITERATE has more parentheses... which from a Lisp point of view makes sense. But for this typical iteration example it looks almost the same, otherwise.

ITERATE has also quite some more capabilities than LOOP, for example it supports accumulation of results from nested loops.

Sometimes I wish for an alternate universe where one of Xerox PARC languages (Interlisp-D, Smalltalk, Mesa, Cedar) had gone mainstream, not AT&T ones.
Mesa and Smalltalk, yes. Interlisp, no. A LISP command line was not the right answer. Storing programs as LISP images was not a great idea. And Do-What-I-Mean was really an error corrector for Warren Teitelbaum's typing errors. I once typed "EDIT" when in the wrong mode, and DWIM printed "=EXIT" and threw me out of Interlisp without saving.

My serious LISP work was done in Franz LISP, which had a more UNIX-like compile and run interface.

Interesting!

I only have experience with Smalltalk (VisualWorks back then).

The Lisp and Mesa/Cedar environments is only due to my archeologist work from programming languages and environments.

http://www.catb.org/jargon/html/D/DWIM.html

      Warren Teitelman originally wrote DWIM to fix his typos and spelling
      errors, so it was somewhat idiosyncratic to his style, and would often make
      hash of anyone else's typos if they were stylistically different. Some
      victims of DWIM thus claimed that the acronym stood for ?Damn Warren?s
      Infernal Machine!'.
  
      In one notorious incident, Warren added a DWIM feature to the command
      interpreter used at Xerox PARC. One day another hacker there typed delete
      *$ to free up some disk space. (The editor there named backup files by
      appending $ to the original file name, so he was trying to delete any
      backup files left over from old editing sessions.) It happened that there
      weren't any editor backup files, so DWIM helpfully reported "*$ not found,
      assuming you meant 'delete *'". It then started to delete all the files on
      the disk! The hacker managed to stop it with a {Vulcan nerve pinch} after
      only a half dozen or so files were lost.
  
      The disgruntled victim later said he had been sorely tempted to go to
      Warren's office, tie Warren down in his chair in front of his workstation,
      and then type delete *$ twice.
`LOOP` is not a particularly well design macro.

And you don't really have to debug the expanded code (unless you want to). It's perfectly possible to retain the original unexpanded source locations for debugging in a properly designed Lisp.

> Because this is a macro, if you have to debug or step, you get to do so through the expanded code. This is usually not fun. But at least it's a memory-safe language, so it's not the hell of fixing C++ template bugs.

So wrong...

  (loop for x from i to 10 do magic (foo))

  Compound form expected, but found MAGIC.
  Current LOOP context: DO MAGIC (FOO).
     [Condition of type CCL::SIMPLE-PROGRAM-ERROR]
The C2 wiki presents (as usually) a very uneducated view about Lisp. I am not going to rip apart the details but whenever you read something about Lisp on the C2 wiki please keep in mind that Lisp is not their usual cup of coffee really.

So if you see something like "Lisp macros could be easily replaced with a magic Ruby block lotion and are just lambdas + syntax", just because nobody on C2 argues with it doesn't mean its not completely ignorant and wrong.

"These guys are ignorant and wrong, but I'm not going to go into details." And Lisp folk wonder why they're perceived as arrogant.
Sorry but I have a day job and can't be asked to set right every piece of wrong information. Just be happy that somebody warned you.

Edit: "Arrrrrr, nasty Lisp-folk, if they weren't just programmers who know Lisp, arrrr."

"This is wrong and I won't tell you why" is not very valuable; opinions are ten-a-penny on the internet.
Wonderful, you criticize him and then you expand and generalize it to 'Lisp folk' being perceived as arrogant.

Then YOU lecture him.

> opinions are ten-a-penny on the internet.

Including your own, it seems.

Flagged for insult.
Flagged for insult.
Then why did you post the original comment?

Besides, your comment's wrong, and people should know that.

No, don't expect me to say way.

Flagged for insult.
So, is no one going to say that this person's comment is laughably wrong?
While you can't replace all macros with blocks, I think there's an interesting argument in there about doing some things done with macros using blocks, and that it has the advantage that a function accepting a block can be passed around in ways a macro can't be.
If we talk about blocks, do we mean something different than a LAMBDA, e.g. a closure?
No. Smalltalk called these blocks, and Ruby does too.

BTW a lambda expression and a closure are not the same thing. A lambda expression evaluates to a closure, just as an expression like "(make-instance 'foo)" (or "new Foo()" for non-Lispers) evaluates to an instance. The former, in each case, is a syntactic entity, something you can write in your program; the latter is an object inside the machine.

A: Presenting macros!

B: Look what you can do with not-macros.

A: Great.

;)

Flagged for insult.