Comment by cryptonector

Comment by cryptonector 21 hours ago

5 replies

These are very good notes. Here's a few of my notes on these notes:

> Naming: Like Unix, Graham likes short names, often to the point of crypticness. See my notes on naming for a better way.

Eh, you should see Haskell, where conventions are to use `x` for an object of some type and `xs` for a list of `x`, for example. And not just x/xs, but h/hs, etc. Once you get used to thinking of polymorphic functions as mathematical functions then this starts seeming ideal rather than cryptic.

> Loops: Graham avoids loop, because it is so easily misused and different from the functional programming style. Sometimes, however, loop is the clearest simplest way to write something.

I agree, but this is probably didactic. Many Lisp teachers insist on students learning to think of recursion as a natural way to do iteration. I'm not sure I agree with that viewpoint, but I don't think it's unreasonable. The issue I have is that one really has to learn how to recognize tail recursion instantly, even mutual tail recursion, and then also how to recognize when non-tail recursion (especially mutual non-tail recursion) gets optimized by the compiler into a loop with an explicit stack (if at all).

> Preference for recursion over iteration, even if it might lead to code that causes stack overflows on very long lists.

Yes, well, see above. I agree that starting Lisp programmers will take some time to learn how to use tail recursion, but it's not a problem for a) an experienced Lisp programmer, b) a Lisp teacher who is trying to drive this point home.

> Backtraces are incredibly useful for debugging. Be aware though that tail-call elimination may have removed some function calls from the stack.

This is true in many languages. I've seen this in C. You have to get used to it because tail call optimization is so important.

> bfs is a terrible name, because it says how it works, not what it does.

Sometimes "how it works" has to bleed into the naming somehow. Generally (i.e., not just in Lisp) this function could be `search` (which is what TFA wants) in some namespace/package/whatever that where the function implements [part of] an interface and the provider's name denotes that this is a breadth-first search, but it's very important to understand the trade-offs of depth-first and breadth-first searching and which you get needs to be observable somewhere.

lapsed_lisper 18 hours ago

IDK how much this matters, but the Common Lisp standard doesn't mandate tail call elimination. So although many implementations offer it (usually as a compiler thing), it's conceptually an implementation-dependent detail that borders on a language extension: if your code actually depends on TCE for correct execution, that code might exhaust the stack under ordinary interpreter/compiler settings, and differently across different implementations. So for Common Lisp, if you want to use standardized language features standardly, it's quite reasonable to reach for iteration constructs rather than tail recursion.

  • cryptonector 15 hours ago

    > if your code actually depends on TCE for correct execution, that code might exhaust the stack under ordinary interpreter/compiler settings

    But Lisp programmers tend to use recursion for iteration and very much count on TCO. So it really has to be implemented, and the language should require it.

    • Jtsummers 15 hours ago

      Counting on TCO is more of a Scheme thing (where the language spec guarantees it) than a Common Lisp thing. CL does not guarantee TCO so, at least historically, looping (various forms, not just the LOOP facility) was quite common.

  • dapperdrake 17 hours ago

    That is why Doug Hoyte builds NAMED-LET.

    • cryptonector an hour ago

      Yes, quite. In Let Over Lambda he builds a code walking named-let macro that transforms tail-recursive loops to iterative loops.