[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)