Hacker News new | past | comments | ask | show | jobs | submit login
Guy Steele Interviews John McCarthy, Father of Lisp (2009) (infoq.com)
118 points by _zhqs on Jan 21, 2015 | hide | past | favorite | 21 comments



People have added some Englishy stuff and at least the syntax of that is not in the spirit of Lisp.

Ha! I knew it! He hated LOOP as much as I do! :-)


There are others who don't hate LOOP.

Me for example. The by far best looping construct is Jonathan Amsterdam's ITERATE. But that is LOOP on steroids. LOOP is surprisingly practical. ITERATE is even more practical and has more parentheses. ;-) But since LOOP is there, it's good enough for a lot of purposes...

There are several objections against LOOP:

* another language. Don't use Common Lisp is embedded languages are a problem. ;-)

* the syntax. Okay.

* Spec is not good enough. For me it is good enough.

* Not enough parentheses -> ITERATE.

* non-extensible -> LOOP variants or ITERATE

I've used all kinds of stuff when it comes to loops and iterations.

Bad: tail recursion. Worse: RECUR in Clojure. Both lead to hard to maintain and hard to read code. I've one believes in Functional Programming, it might look attractive. But I don't buy that. Having read and refactored a lot of these constructs, I find them hard to maintain.

LOOP: very useful in many cases.

Series: hard to extend

ITERATE: cool, everything I want.

Btw., LOOP comes from Interlisp from the 70s and was called FOR. The idea was to have english-like embedded languages. It was then moved and extended to Maclisp, and from there to Lisp Machine Lisp and then Common Lisp.


The common-lisp.net page for ITERATE has this example for finding the longest list in a list of lists:

  (iterate (for elt in list-of-lists)
           (finding elt maximizing (length elt)))
But what's wrong with this?

  (reduce (lambda (a b) (if (> (length b) (length a)) b a))
          list-of-lists)
Okay, you have to do a little more work if you only want to take the length of each list once instead of twice. But if you don't care about that, this is quite elegant and introduces no new vocabulary. (Unless the lists are very long, calling LENGTH twice isn't anywhere near twice the cost of calling it once, because of cache effects.)


The reduce code is just not very declarative. I have to inspect the code and i need to have a mental model of REDUCE (which is not trivial). What is b and what is a? I need a mental model of ITERATE too, but the declarative wording gives hints what it is supposed to do.

Code like above is simply less readable. There are lots of uses of REDUCE which are simple to understand. Code like above, less so - where I need to write a special purpose function.

Another problem is that you need to think about the corner cases:

    (reduce (lambda (a b) (if (> (length b) (length a)) b a))
            '())
-> Error

Also you can see that the REDUCE version is slower. Usually there will be a function call overhead - unless the compiler has some optimization for that in some optimize level. Also 'LENGTH' is called multiple times on the longest list so far. You could work around that in REDUCE ... In ITERATE this is not needed: it calls LENGTH only once per list item.


You always need to think about the corner cases. What is the longest element in an empty list of lists? It might be better to get an error in such a case than to have an empty list returned, since that's not an element of the input list, in violation of one possible interpretation of the function's specification.

> Code like above is simply less readable.

I write code like this all the time, and have for 35 years. I think it's perfectly readable.


I don't know it enough to love or hate it, but I'm reading one of Paul Graham's books where he says (paraphrasing) "Don't worry if you don't understand LOOP; no one understands LOOP."


When I was part of the social community that included most of the Lisp Machine hackers including several superb macrologists, call it 1980-3, I don't remember any other single thing generating more discussion. Not about it being bad, or dangerously over-complicated, just what it should do and how.

So, yeah. I don't remember using it more than once in my own code.


I should write a nice long blog post on how to iterate in CL without using (full) LOOP. (I have no problem with the trivial CLtL1 form of LOOP.) There's DO, there's MAPCAR, there's REDUCE, there's REMOVE-IF-NOT and the like, and these days we often have the option of using tail recursion. I believe that different kinds of loops should be written with different constructs, as this gives the reader a clue up front about the nature of the loop. If you see a MAPCAR, for example, you can assume (well, you should be able to assume) that the iterations are independent; there is no data flow from one invocation of the mapped function to another, so the ordering of the input list(s) affects only the ordering of the result list, not its contents. Furthermore, any loop with that property should be written as a MAPCAR so the reader can tell that.

The point, of course, is that trying to create "one macro to rule them all" is already wrongheaded. There shouldn't be one universal iteration construct.


>The point, of course, is that trying to create "one macro to rule them all" is already wrongheaded. There shouldn't be one universal iteration construct.

I agree that MAPCAR et al. should be used when they're appropriate. They are, however, sometimes simply not, and it's important to be able to build code to handle those cases. You don't need LOOP for that, since you can always use TAGBODYs and GOs, but LOOP is almost certainly more appropriate, just as MAPCAR et al. are usually more appropriate than LOOP.

In other words, yes, there should be a universal iteration construct; there just shouldn't be only a universal iteration construct. There's nothing wrong with having multiple tools at different abstraction levels for the same group of tasks, and I'd argue that it's usually a good thing.


I agree it's great to have multiple tools; and obviously at the low level there has to be something universal that everything else is implemented with, whether that's TAGBODY/GO, DO, or tail recursion.

But I still think LOOP tries to be the solution to too many problems. Worse, it attempts to replace compositionality -- the rules of the language by which more complex expressions are built up from simpler ones -- with vocabulary.

Oh well. I've made my case -- I'll shut up now :-)


I agree with you to some degree. Higher order functions should be used wherever possible and there should be some alternatives to LOOP for the common cases such as iterating over a range of numbers. It is much easier to understand patterns when they are built into the language than when they are written out using LOOP.

The thing is LOOP is very good at creating ad hoc kinds of loops. Say you are writing a loop to do password validation. Repeat at most N times and then lock the account or go through once a correct password is entered. That kind of iteration is too specific for pretty much any kind of looping construct besides LOOP.

As I see it, LOOP is not about creating "one macro to rule them all", it's about creating a macro that's a catch-all. One that you can always use when nothing else quite describes what you want.

I'm just thinking out loud.


I would probably use DO for the password validation example. It's strange to me that people think of DO as complicated; there is a little complexity there, to be sure, but much less than for LOOP. Once you understand that the inits and updates are done in parallel, and you get where the exit test goes, you've got it; there are no more rules to learn.

I understand that LOOP seems simple in simple cases, but that simplicity is evidently deceptive, as attested by the comments I was replying to.


Makes sense. It's just to me, as someone who knows LOOP that

  (loop repeat n
        thereis (valid (read-password))
        finally (lock-account))
seems simpler than

  (do ((i 0 (+ i 1)))
      ((>= i n) (lock-account))
    (when (valid (read-password))
      (return t)))
You are definitely right that loop is deceptively complex. In the code I gave, a reader would need to know that thereis skips the epilogue when terminating the loop (I would comment the code if it was going to be seen by others). I'm now finding it incredible how much lingo I used in the last sentence.


Personally I think LOOP is a fantastic example of how great and terrible macros in Lisp can be.

To be able to produce a syntax abstraction like LOOP within the language is an example of mind boggling power available (excuse my minor hyperbole). But conversely one should also take it as an equally valid example of where restraint in the use of such power should be diligently exercised.


> The point, of course, is that trying to create "one macro to rule them all" is already wrongheaded. There shouldn't be one universal iteration construct.

Tail-calls fit the bill. (And you can build things like reduce on top of them as normal functions.)


Do you have a blog? I'd love to read that post if you write some day.

Although I personally use loop and eschew other iteration constructs I see the point you are getting at, it communicates better the intent.


Haaa he was talking about the loop macro. I misinterpreted (sic) as Common Loops, thinking the 'englishy' part was referring to the subject - verb nature of OOP.


"I started on [Lisp] during late summer when I was at IBM and I decided to write a program for differentiating algebraic expressions. What I noticed is that I could write conditional expressions, which I'd already used in those legal move routines for the chess program and that they could be used to embody the rules that were given in the calculus text, in an extremely direct form. Differentiation of algebraic expressions was the leading example that led to the functional form of Lisp programs."

I'm not very familiar with Lisp. Does anyone have a solid enough idea what he's talking about here to be able to give possible concrete examples?


See https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-16.htm... for sample code demonstrating how to implement symbolic differentiation in Lisp.


The original AI memos at MIT show almost step-by-step how McCarthy developed Lisp. Here's the first, which includes symbolic differentiation as an example: http://www.softwarepreservation.org/projects/LISP/MIT/AIM-00... . For the rest, skip down to the Documentation and Papers section of http://www.softwarepreservation.org/projects/LISP/lisp15_fam... .





Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: