[plt-scheme] Re: to define, or to let (last try)

From: Bradd W. Szonye (bradd+plt at szonye.com)
Date: Tue Apr 27 17:01:16 EDT 2004

Joe Marshall wrote:
>> When I see a fragment of code written thus:
>> (foo (compute-a) (compute-b))
>> Then I know that it is equivalent to this:
>> (let ((arg2 (compute-b)))
>>    (foo (compute-a) arg2))
>> or vice versa.

Paul Schlie wrote:
> Please, you've got to be kidding - ... they're equally ambiguous if
> compute-a/b are interdependent. (so in truth, your presumed
> equivalence doesn't hold in R5RS)

According to R5RS, "The order of evaluation may be chosen differently
for each procedure call." Therefore, Scheme programs cannot portably or
reliably assume that the call to FOO evaluates its arguments in any
particular order. Programs that do make that assumption are unportable
at best and incorrect at worst.

This does not limit our ability to write portable or reliable programs,
however, because there are ways to express that dependence in reliable
and portable ways. For example, you can use BEGIN or LET* to make sure
that side effects take place in the order you require.

Depending on argument evaluation order is nonportable, unreliable, and
completely unnecessary. Furthermore, the refactoring Joe did above is a
good example of the kinds of things maintenance programmers do. He's
entirely justified in assuming that the order doesn't matter -- unless
it's unreliable, non-portable code, in which case it really isn't his
fault if he breaks something.

The existence of separate constructs for sequential and non-sequential
evaluation makes it possible to do that kind of maintenance task without
worrying that you'll introduce a bug; at worst, you'll unmask an
existing bug. Furthermore, the separate constructs make it possible to
*test* for those latent bugs, because automated testing tools can tell
whether code is supposed to depend on evaluation order.

Anton argues that you can get the best of both worlds by fixing the
evaluation order but keeping the separate constructs. It's a good idea
in theory, but in practice it just encourages the elimination of the
sequencing forms. I've seen too many people claim that they will only
use LET* if scoping (not sequentiality) requires it, and apparently
LETREC* disappeared from PLT Scheme because it was "redundant."

I don't understand why some folks want fixed evaluation order so badly.
It's trivial to fix the order when you need it, and you're already
screwed if you don't know whether you need it. If you really, really
want fixed order all the time, restrict yourself to the subset of Scheme
that guarantees it. Programs written in that subset are much harder to
modify and maintain -- which is exactly why I don't want it creeping
into the rest of the language!
Bradd W. Szonye

Posted on the users mailing list.