10

A few questions here, regarding letcc that is used in The Seasoned Schemer.

(define (intersect-all sets)
  (letcc hop
    (letrec
      ((A (lambda (sets)
            (cond
              ((null? (car sets)) (hop '())
              ((null? (cdr sets)) (car sets))
              (else
                (intersect (car sets)
                           (A (cdr sets)))))))
       ; definition of intersect removed for brevity
      (cond
        ((null? sets) '())
        (else (A sets))))))
  1. I think I understand what letcc achieves, and that is basically something like catch and throw in ruby (and seemingly CL), which basically means a whole block of code can be cut short by calling whatever the named letcc is. This feels like the least "functional" thing I've come across in this short series of books and it makes me feel a bit hesitant to use it, as I want to learn a good functional style. Am I just misunderstanding letcc, or is it not really a functional programming concept and only exists to improve performance? The whole idea that I can be in the middle of some routine and then suddenly get to another point in the code feels a bit wrong... like abusing try/catch in Java for program flow.

  2. letcc doesn't seem to exist in the version of guile (1.8.7) I have installed in OS X. Is there another name for it that I should be looking for in guile?

  3. If I'm misunderstanding letcc by comparing it with try/catch in Java, or catch/throw in ruby (which is not exception handling, just to be clear, for the non-rubyists), how exactly does it work, at the functional level? Can it be expressed in a longer, more complex way, that convinces me it is functional after all?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
d11wtq
  • 34,788
  • 19
  • 120
  • 195
  • 1
    If you have an implementation that supports both "normal" continuations and escape continuation it is normally cheaper to use the latter. The pattern in your example is a natural fit for escape continuations. The standard abbreviation is `let/ec`. – soegaard Jun 19 '12 at 20:36
  • FYI, [this page](http://community.schemewiki.org/?seasoned-schemer) shows how to define `letcc` in terms of `call/cc` using a simple macro. –  Jun 20 '12 at 10:31
  • @JonO, I read that before and tried it, but guile just says `ERROR: invalid syntax ()` `ABORT: (misc-error)`. Checked I've done it the way the page says, but it still doesn't work. I'll probably figure out why when I get to learning macros. – d11wtq Jun 20 '12 at 11:50
  • 1
    Well who knows where this is documented, but `(use-syntax (ice-9 syncase))` is needed before defining macros (at least the way that one is defined) :) – d11wtq Jun 20 '12 at 12:23

1 Answers1

13
  1. "Functional" has several meanings, but no popular meaning contradicts continuations in any way. But they can be abused into creating code that is hard to read. They're not tools that can be "abused for program flow" -- they are program flow tools.

  2. Can't help you there. I know that there was semi-recent on continuations in Guile, but I don't know where things stand. It should definitely have call-with-current-continuation, usually also under the more friendly name of call/cc, and let/cc is a simple macro that can be built with call/cc.

    I can tell you that in Racket there is a let/cc builtin together with a bunch of others builtins in the same family, and additionally there's a whole library of various control operators (with an extensive list of references.).

  3. Simple uses of let/cc are indeed similar to a catch/throw kind of thing -- more specifically, such continuations are commonly known as "escape continuations" (or sometimes "upward"). This is the kind of use that you have in that code, and that is often used to implement an abort or a return.

    But continuations in Scheme are things that can be used in any place. For a very simple example that shows this difference, try this:

    (define (foo f) (f 100))
    (let/cc k (+ (foo k) "junk") (more junk))
    
  4. Finally, if you want to read more about continuations, you can see the relevant parts of PLAI, there's also a more brief by-example overview that Matthew Might wrote, and you can see some class notes that I wrote that are based on PLAI, with some examples that were inspired by the latter post.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
  • 2
    Wow, after reading that article by Matthew Right, my opinion has changed. I think actually understanding how `call/cc` works makes it seem a lot cleaner than I was originally thinking. Huge thanks for the explanation. Turns out my version of guile has `call/cc` and `call-with-current-continuation`, but no `letcc`. Also, `call/cc` is a lot more interesting than `catch/throw` in ruby, as it lets you do much cooler things. I guess The Seasoned Schemer will explore this in more detail in later chapters. – d11wtq Jun 20 '12 at 08:09
  • 1
    If you're done with his text, then have a look in my class notes too: there are several examples that are much more extended, with a complete development of generators and amb, and it starts with a description of CPS -- the way you'd implement some of these in other languages. – Eli Barzilay Jun 20 '12 at 15:08
  • 4
    And BTW, his name is Matthew Might. (He might be right, but his name is Might.) – Eli Barzilay Jun 20 '12 at 15:08
  • `let/cc` are actually `goto`s on steroids. If abused they'll be worse than `goto`s. Of course, there are legitimate cases for `let/cc` the same way there are legitimate (restricted) uses for `goto` such as `switch`. – BlackVegetable Jan 30 '14 at 01:44
  • A straightforward [implementation of letcc and throw in Haskell](https://github.com/dwayne/eopl3/commit/0f5125047e9e3031d6272c4d16270287daf66f2e) that may help someone understand how `letcc` works. – Dwayne Crooks Aug 10 '22 at 12:29