[racket-dev] internal-definition parsing

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Wed Jul 7 13:13:31 EDT 2010

1. My most important concern is that begin-with-definitions takes Nx times more time to expand than the current body expander. Are you going to use this macro or are you going to bake it all into the implementation? 

2. My second one is one of internal interest. In principle all 'body' forms should be expanded in one single place and this should be a single point of change here. Is this correct? 

3. Historically our original form borrowed from Algol. I don't think Chez had this liberal notion. 

4. I am vaguely interested in #%body because I have a hunch that there are yet more possibilities. But at the same time, I understand your objection and I can only see a limited utility of #%body now. (That doesn't mean much, because I originally failed to appreciate #%module-begin.)

-- Matthias

On Jul 7, 2010, at 12:23 PM, Matthew Flatt wrote:

> Short version:
> I'm planning to change internal-definition expansion (anywhere that
> says `body ...' in the Racket documentation) to allow expressions to
> mingle with definitions. For example,
>  (let ()
>    (define (f) x)
>    (displayln f)
>    (define x 1)
>    (list f x))
> would be allowed; the `displayln' call would print `#<procedure:f>',
> and the result would be a list containing the function `f' and the
> number 1.
> Some other changes related to internal-definitions have been suggested,
> but I don't plan to implement them for now.
> ========================================
> Long version:
> Mixing Expressions and Definitions
> ----------------------------------
> Long ago, internal-definition positions in MzScheme allowed multiple
> sets of definitions separated by expressions. For example,
> (let ()
>   (define x 1)
>   (display x)
>   (define y 2)
>   (display y))
> was allowed. In that old mode, the definition of `y' started a new
> group of definitions, so the right-hand side of `(define x ...)' could
> not refer to `y'. In other words, the above was equivalent to
> (letrec ([x 1])
>   (display x)
>   (letrec ([y 2])
>     (display y)))
> I think this behavior mimicked Chez Scheme at the time, but I may be
> mistaken. For whatever reason (I don't remember), we changed
> internal-definition positions to now allow additional definitions after
> an expression. Maybe it was to more closely match R5RS.
> Meanwhile, `unit', `module', `class' and other forms evolved to allow
> expressions mixed with definitions. Probably as a result, many have
> suggested that internal definitions similarly allow expressions mixed
> with definitions --- without the old grouping. In that case, the `let'
> above would be equivalent to
> (letrec-values ([(x) 1]
>                 [() (begin (display x) (values))]
>                 [(y) 2])
>   (display y))
> This change seems like a good idea. Now that I've finally gotten around
> to trying it out, I think we should go with it immediately.
> Should an expression be required at the end? A `module', `unit', or
> `class' body can consist of just definitions. Similarly, if an
> internal-definition context ends with a definition, we could define the
> result to be `(void)', which is what the `block' form does.
> I think it's better to require an expression at the end, partly on the
> grounds that the internal-definition block is supposed to return a
> value (unlike the body of `module', etc.) and partly to avoid making
> forms like
> (define (f x)
>   x
> (define (g y)
>   y))
> legal when they are almost certainly mistakes.
> This change could be implemented in new `lambda', etc. bindings, but I
> think existing forms in `racket' should change. Furthermore, to keep
> things simple, the existing `scheme' forms --- most of which are the
> same binding as the `racket' forms --- should also change.
> The change should not affect the `r5rs' language, and we should change
> the `r6rs' language so that it doesn't inherit the more liberal
> handling of internal-definition forms.
> There are no issues with backward-compatibility for existing `scheme'
> and `racket' modules, as far as I can tell. The change would just
> accept more modules.
> More Internal-Definition Contexts
> ---------------------------------
> Internal definitions could be allowed in more places, such as
> (define f
>   (define x 2)
>   x)
> In principle, where a single expression is expected, definitions could
> be allowed to precede the expression.
> It's a tempting generalization, but probably too confusing. I think
> it's better to use some form that groups definitions with an
> expression: `(let () ....)', `(block ....)', or something like that.
> An Implicit Internal-Definitions Form
> -------------------------------------
> Many forms --- including `lambda' `let', and the function-shorthand
> variant of `define' --- support internal definitions. The handling of
> internal definitions is currently tired to the form.
> An alternative would be to have those forms implicitly wrap a group of
> internal-definition forms with `#%body', in much the same way that an
> application form is converted to a use of `#%app'. Then, the treatment
> of internal definitions could be independently configured through a
> module import. For example, one module might use a `#%body' that
> corresponds to the old handling of internal definitions, while another
> module could import a `#%body' that corresponds to the new handling.
> Setting aside the backward-compatibility issues of adding an implicit
> form, I think it turns out not to work as well as `#%app'. The problem
> is that there's not a good place to get the lexical context to use for
> the implicit `#%body'. We have many macros analogous to
> (define-syntax-rule (squawk body ...)
>   (begin
>     (displayln "squawk!")
>     (let () body ...)))
> If you take the lexical context from the parentheses that surround the
> `let' form, then the `#%body' is drawn from the context of the `squawk'
> definition, while the intent was more likely to get it from the use of
> `squawk'. Meanwhile, the `body ...' sequence itself doesn't necessarily
> have a lexical context (since it doesn't have a parenthesis, roughly),
> and the sequence is recreated by the macro implementation anyway.
> This problem happens occasionally with `#%app'. When a use of
> identifier macro expands in an application position, for example, the
> implementor of the identifier macro usually should copy the lexical
> context of the old application form to the expansion result. Such cases
> are more rare than examples like `squawk', though.
> So, an implicit `#%body' form seems like a good idea in principle, but
> it doesn't seem to work out well with our current syntax. I'm currently
> inclined to not change Racket and to treat this as something to support
> better the next time we define a core syntax, but I'm interested in
> further discussion.
> _________________________________________________
>  For list-related administrative tasks:
>  http://lists.racket-lang.org/listinfo/dev

Posted on the dev mailing list.