[plt-scheme] to define, or to let

From: Anton van Straaten (anton at appsolutions.com)
Date: Mon Mar 22 15:16:43 EST 2004

Matthew Flatt wrote:
> I'm with Paul. I understand portability concerns, but not the idea that
> unspecified evaluation order is good in practice. Indeed, it seems
> strange to me that (in this thread) fixed order of evaluation has
> become identified with theory, while unspecified order of evaluation
> has become identified with practice. I'm more used to non-determinism
> as a theoretician's game.

The reason for the identification with theory is that the arguments for
fixed evaluation have been coming from the the theoretical position of
increased safety, determinism, verifiability etc.  The same sorts of
arguments have also been made by Matthias Blume about SML/NJ, for example.

These arguments are sound as far as they go, which is that within the
theoretical domain of a language with fixed order of evaluation, these
desirable properties are indeed all improved.  However, the problem is that
such a language is incapable of expressing distinctions that still exist in
actual programs in the language.  Fixed order of evaluation in a language
"solves" a problem simply by dropping the ability to distinguish, in source
code, between legal code and potentially buggy code (code which
inadvertently depends on order of evaluation).

So, it's not so much that "unspecified evaluation order is good in
practice", although you can put it that way, and it's true.  The real point
is that inadvertent evaluation order dependencies can lead to bugs in
practice, whether or not a language fixes evaluation order.  A fixed
evaluation order doesn't fix those bugs, it merely hides them, making buggy
code indistinguishable from legal code at the source code level.

> Or a compiler writer's game. C++ has unspecified order of evaluation,
> and it's hardly the domain of theoreticians. But, as in Scheme, I think
> this is a concession to compiler writers, and not a matter of making
> the code more clear. I prefer to leave unspecified order of evaluation
> (along with operator precedence) to C++ quizmasters in need of
> surprising bits of code.

The point is that those surprising bits of code tend to be poor practice in
any language, regardless of evaluation order.  The fact that an expression
like "x + x * ++x" has a well-defined result in Java, whereas it doesn't in
C++, is beside the point - in both languages, it's poor practice.  Almost
any rearrangement of the Java version of the expression will produce a
different result - so even simple expressions and function calls aren't
referentially transparent.  Code like this increases the fragility of a
system, and reduces its ability to support refactoring, whether manual or
automatic.

Giving such expressions well-defined results in the language doesn't solve
any real problem, it simply means that bad practice is given a consistent
interpretation, so that bugs will behave repeatably (hopefully).  If that
were all it did, it might be OK, but more seriously, it removes the ability
to express the intent of the programmer, so it becomes impossible for a
reader of the code to determine whether an order dependency is intentional,
thus much harder to detect unintentional dependencies.

I think what Matthias F. said about "we must be able to equip the language
with tools that help us discover errors and that help us describe our
thoughts" is right on point here: this is both about describing the
programmer's thoughts, and about discovering errors.  Languages with fixed
evaluation order remove the ability to describe a certain kind of thought -
which is bad - and they make discovering a certain class of errors harder -
which is bad.

> I can see why a programmer might want to specify that certain
> computations are truly independent (i.e., they could be evaluated in
> parallel).

Forget about parallelism, it's cool but not really relevant.  There are
many, many places in any code where order dependence is not required - so
much so, it makes sense as the default case, as C++ and Scheme both
demonstrate.  Since order dependence is inherently more fragile than
independence, it makes sense to support the expression of the distinction
between the two cases.  That way, if you inadvertently introduce a
dependency unintentionally, you or someone else have an improved possibility
of spotting that error, and being able to do something intelligent about it.

> In any case, I don't think that a plain open parenthesis ---
> or even a `let' form --- is the right way to express this intent.

The intent being expressed by the plain open parenthesis or let form is that
this is purely a function call or binding operation, not a combined function
call + sequencing operation, or binding + sequencing operation.  Sequencing
should not be the default scenario!  It's fragile, undesirable, and most of
the time, unnecessary.  If programmers want to rely on sequencing - which of
course is still useful, at times - they should have to say so,
unambiguously.

Anton



Posted on the users mailing list.