[HN Gopher] Emulating Goto in Scheme with Continuations
       ___________________________________________________________________
        
       Emulating Goto in Scheme with Continuations
        
       Author : usually
       Score  : 43 points
       Date   : 2026-02-19 17:58 UTC (4 days ago)
        
 (HTM) web link (terezi.pyrope.net)
 (TXT) w3m dump (terezi.pyrope.net)
        
       | taeric wrote:
       | I confess I like Common Lisp's TAGBODY far more than I feel like
       | I should. Having constrained GOTO semantics to a short section of
       | the codebase is surprisingly useful.
        
         | kccqzy wrote:
         | Constrained GOTO semantics sounds a lot like delimited
         | continuations. Indeed I think Scheme continuations are a little
         | too powerful for regular use by having the possibility of
         | global effect (like longjmp). Delimited continuations make the
         | effect more local.
        
           | richdougherty wrote:
           | If you'll excuse the self-post, here's a blog post on goto
           | with delimited continuations.
           | 
           | https://rd.nz/2009/03/goto-in-scala.html
           | 
           | It uses an experimental compiler plugin for the Scala
           | compiler. It's typesafe at compile time. At runtime
           | unfortunately it relies on exceptions for control flow.
        
           | taeric wrote:
           | Delimited continuations always bounced off of me. In theory,
           | they should be a lot like coroutines? I think, in practice, I
           | just never really internalized all that goes into managing
           | the current "environment" for a piece of code that is managed
           | by the call state.
           | 
           | Like, I have a few partial mental models for everything that
           | they pull together. I haven't really tried to build on that,
           | though. Should put some time to that.
        
             | mikkupikku wrote:
             | You could implement coroutines with deliminated
             | continuations, which is probably the best way to use
             | deliminated continuations.
        
         | Joker_vD wrote:
         | Do you use it mostly as "labeled breaks" or to throw the values
         | out of closures?
        
           | taeric wrote:
           | I haven't used it much at all, to be clear. I just found it
           | surprisingly fun the last few times I played with it.
           | 
           | The specific thing it made a lot easier was implementing
           | algorithms the way that Knuth writes them down. Which is very
           | much a set of steps with specific calls on what step to go to
           | next.
           | 
           | I think the reason I found it fun to play with was that I
           | found that style of laying out what needed to be done was
           | easier to work with than the standard breakdown that making
           | everything a function or an object seems to require. For me,
           | it was a lot easier.
           | 
           | Edit: I have one of the times I used this here:
           | https://taeric.github.io/many_sums.html I did not put any
           | effort into cleaning up that code, though. So it can probably
           | work as an argument in either direction. :D
        
         | pmcgoron wrote:
         | From John Cowan:
         | 
         | TAGBODY doesn't actually require continuations, delimited or
         | undelimited, just proper tail calling. A macro can rewrite each
         | section of the TAGBODY into a procedure nested within a `let`
         | that tail-calls its successor, and the body of the `let` tail-
         | calls the first procedure. (GO tag) is then equivalent to just
         | (tag). This is a great way of doing state machines. Chicken has
         | a tagbody egg, I think.
        
         | adrian_b wrote:
         | Following the recommendations of Knuth, the language Mesa,
         | which was implemented at Xerox during the seventies, and which
         | was a source of inspiration for various later languages,
         | including Modula, Ada and Python, included a form of
         | "restricted GOTO" which is the most useful kind of GOTO in my
         | opinion.
         | 
         | The Mesa restricted GOTO allowed jumping forwards, but not
         | backwards, and it allowed jumping towards an outer block, but
         | not towards an inner block.
         | 
         | These 2 restrictions eliminate all the "harmful" features of
         | the traditional GOTO, while retaining its advantages for
         | handling exceptional conditions or for terminating multiple
         | levels of nested program structures.
         | 
         | The Common Lisp TAGBODY appears to be only partially
         | restricted, by allowing backward jumps, so it does not prevent
         | the kind of hard-to-understand program structures for which
         | GOTO was criticized.
         | 
         | GOTOs in random directions may be used to implement state
         | machines, but such state machines can still be implemented in a
         | language with restricted GOTO by not using GOTO, but by using
         | mutually recursive procedures, if tail-call optimization is
         | guaranteed.
        
           | taeric wrote:
           | I'm not clear that jumping backwards is that tough to reason
           | with. Notably, Knuth's algorithms do that quite commonly,
           | right?
           | 
           | I do think they need to be somewhat constrained to not jump
           | to places that need new things initialized. Which, it is
           | truly mind blowing to know folks used to just jump straight
           | into other functions. Mid function. Because why not.
        
       | umanwizard wrote:
       | Note that the example program:                 (define (displayln
       | obj)         (display obj)         (newline))
       | (define cont #f)              (displayln         (call/cc
       | (lambda (k)             (set! cont k)             "cont set")))
       | (begin         (displayln "procedure called")         (displayln
       | "after procedure call")         (cont "continuation called")
       | (displayln "after continuation call"))
       | 
       | will only terminate if pasted into a REPL, not if invoked from a
       | file.
       | 
       | This is because every top-level form in the REPL has an implicit
       | continuation "return to the REPL and read more input".
       | 
       | So after "continuation called" is printed, we go back to the
       | prompt and await more input.
       | 
       | However, if this code is saved in a file and you run it (e.g.
       | "guile my-script.scm") then the continuation of the top-level
       | `displayln` call is the top-level `begin` form, and we enter an
       | infinite loop.
        
       | soegaard wrote:
       | If you are into continuations, check Friedman's papers on
       | ReadScheme.
       | 
       | https://github.com/schemedoc/bibliography/blob/master/page6....
       | 
       | In particular look at "Programming with Continuations", "Engines
       | Build Process Abstractions" and "Continuations and Coroutines".
        
       | Trung0246 wrote:
       | Here's my very funny implementation of delimited continuation in
       | plain C with zero ASM usage (with help from a specific compiler
       | built-in)
       | 
       | https://gist.github.com/Trung0246/8f801058212d3bbbef82690a31...
       | 
       | Demo (old version with outdated compile flag but still works):
       | https://godbolt.org/z/n9ch4TM3s
       | 
       | It works for gcc/clang/msvc with -O0, -O1, -O2, and even -O3
        
       ___________________________________________________________________
       (page generated 2026-02-23 23:00 UTC)