[racket-dev] `letrec' and continuations

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Jul 8 15:43:06 EDT 2011

I've implemented (not yet pushed) a revision of internal-definition
expansion. The `quicksort!' functions at the start of this thread (see
http://lists.racket-lang.org/dev/archive/2011-May/006547.html) run the
same with that change.

Definitions are partitioned into minimal sets that satisfy the
following rule: if a definition's binding is referenced (after all
macro expansion) by an earlier definition, the two definitions and all
in between are in the same set. If a set contains a single definition
that does not refer to itself, then it's a `let' layer, otherwise the
group is a `letrec' layer.

Actually, I didn't change internal-definition expansion. I changed
`letrec-syntaxes+values' while leaving `letrec' along. That is,

 (letrec-syntaxes+values () clauses body ...)

is not necessarily the same as

 (letrec clauses body ...)

Making them different is a little awkward, but it solves a problem with

At Fri, 20 May 2011 09:28:40 -0700, Matthew Flatt wrote:
> At Fri, 20 May 2011 11:03:04 -0400, Sam Tobin-Hochstadt wrote:
> > On Fri, May 20, 2011 at 10:53 AM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
> > > I like the idea of adjusting the semantics of internal definitions and
> > > leaving `letrec' alone.
> > 
> > While this seems like a nice change, how does it interact with
> > internal syntax definitions?  If there are internal syntax
> > definitions, do we fall back to `letrec-syntaxes+values'?
> Good question. Yes, I think an internal syntax definition would have to
> be treated like a definition that refers to the last binding in the set
> of definitions.

No, it's no good to "give up" when variable definitions are mixed with
syntax definitions. For example (and possibly what Sam was thinking
of), internal-definition positions that have Typed Racket declarations
correspond to mixtures of variable and syntax definitions. We certainly
want an improvement of internal definitions to work in Typed Racket.

For fully expanded programs, mixing variable and syntax definitions
causes no trouble. The problem is with `local-expand', which is
supposed to preserve `letrec-syntaxes+values' in an expansion, and
Typed Racket relies on that property of the expander. As Sam noted, we
could introduce `letrec-syntaxes+let*-values', but that breaks existing
tools. Changing the semantics of `letrec-syntaxes+values' is more
backward-compatible in practice.

For those rare cases where you want the old `letrec-syntaxes+values'
(e.g., the expansion of `#%stratified-body'), it's easy to force the
old semantics by adding an initial binding clause

   [() (begin (if #f id (values)))]

where `id' is the last variable in the sequence of bindings. Then, all
bindings are forced into a single `letrec'-like set.

Posted on the dev mailing list.