[racket] multiple-value sugar in "let"* forms

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Mon Jun 4 07:02:19 EDT 2012

Eli Barzilay wrote at 06/03/2012 11:25 PM:
> The way I see it, there are several problems with internal `define's
> as they currently stand, in decreasing order of importance:
> 1. Easy to make hard-to-find bugs in both code refactoring and in new
>     code.

I think that a lot of these problems would be avoided if in most cases 
we could get rid of toplevel-like magic for internal bindings, and also 
have the internal "define" form have syntactic terms to make its scope 
explicit (and control flow can just proceed into "SCOPE-BODY" from the 
context in which the "define/scope" appears):

(define/scope ID VAL-EXPR SCOPE-BODY ...+)

Since each such binding form in a series, even when such bindings don't 
depend on each other, would increase rightward drift, since they'd have 
to be nested, the form should accommodate multiple simultaneous bindings 
(simultaneous wrt both "VAL-EXPR"s and bindings):

(define/scope ((ID VAL-EXPR) ...+) SCOPE-BODY ...+)

For non-simultaneous bindings, in which a "VAL-EXPR" can refer to a 
previously-encountered "ID", we can avoid the rightward drift of nesting 
by introducing an additional form:

(define/scope/sequenced ((ID VAL-EXPR) ...+) SCOPE-BODY ...+)

For the occasions on which we want to bind mutually-recursive internal 
procedures simultaneously, with some magic approaching that of toplevel, 
we should warn of that fact with an identifier we don't like to type as 

(define/scope/here-there-be-dragons ((ID VAL-EXPR) ...+) SCOPE-BODY ...+)

For recursive algorithms, someone suggested that it would be very 
convenient if "SCOPE-BODY" could treat the binding context as a 
procedure, providing different values for the "ID"s, so we get an 
additional form:

(define/scope/proc PROC-ID ((ID VAL-INIT) ...+) SCOPE-BODY ...+)

Now we just need shorter names for these different forms.

> 2. Lack of ability to mimic the `let*'-with-repeated-name idiom.
> 3. Reduces the "verbosity" when measured in nestings, but tends to add
>     more text overall; in addition, the indentation of the named
>     expression is usually the same because `define' is so long; and in
>     addition, it's common to type many definitions, and as much as I
>     use a sophisticated editor I end up typing it verbatim a lot.

The internal "define" verbosity increases more in syntax transformer 

> Where the gaps are substantial (that is, #1 is much more important IMO
> than #2, which is much more important than #3).
> Such a `def+' (which is subtly different from the previously mentioned
> `define*') is something that I'm not sure that the definition-context-
> police will let through, but having #2 seems still important enough
> that I worry that it won't become popular and therefore the #1 point
> is still pending.

I can see why some people want to have internal "define": it looks 
friendlier, you can toss in a binding in many places without having to 
think too much about scope or nest your parens, and you don't have to 
tell newbies that they need different syntax for internal bindings (even 
though the semantics might be more different than they assume).

I'm also wary of the pitfall of inflexibly clinging to the familiar.  
I've been using "let" in Scheme descendants for a decade, and in other 
Lisp descendants for a decade before that, so I'm very comfortable with 
"let"*.  The relative advantages of "define" would have to be big for me 
to choose to give up the relative advantages of "let"* to which I've 
become accustomed.  I don't actually know how you'd improve "define" to 
make it more compelling to me.

I asked about multiple-value "let"*.  All I wanted was to renovate my 
kitchen, but instead you're trying to sell me a downtown penthouse.  
You're going to have to sell me on why I'd want the downtown lifestyle, 
and allay my concerns about social diseases.  We could just renovate my 
kitchen *while* you refine the downtown living story.

Neil V.

Posted on the users mailing list.