[racket-dev] internal-definition parsing

From: Robby Findler (robby at eecs.northwestern.edu)
Date: Wed Jul 7 12:38:38 EDT 2010

Yeah!

(And I think you're right about not having an implicit #%body, but I
don't really know for sure.)

Robby

On Wed, Jul 7, 2010 at 11:23 AM, Matthew Flatt <mflatt at cs.utah.edu> 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.