[comp.lang.c] Expression Based Language

eric@snark.UUCP (Eric S. Raymond) (01/05/89)

In article <9310@ihlpb.ATT.COM> nevin1@ihlpb.UUCP (55528-Liber,N.J.) writes:
>This (using expression forms) won't work too well in (full) C. The problem is
>that pure expression-based languages (like LISP) tend to be typeless as well.
>Having to return a specific data type severely limits the usefulness of making
>control-flow constructs (if, switch, for, etc.) expression-oriented.

To see that this is false, try a simple thought experiment. Imagine that you
have modified your C compiler so that the language now has two new properties;

1. Every block contruct { <st1>; <st2>; .... <stn> ;} returns, as an assignable
   rvalue, the value of the nth statement <stn>.

2. The for, while, do, and case constructs return the value of the last block
   executed before termination; that is, the value of the last *statement*
   executed before termination.

Voila! Expression-oriented C. Trivial to implement, and *incredibly* useful
(if you've ever written LISP you know why). Look at what we could *drop* from
the language -- the sequential-execution comma and ?: operators, for starters.

If you're willing to break some old code, try thinking of function-argument
comma as an aggregation operator...then consider what foo = {4, 5, 2} might
mean for foo of type, say, (int [3]). First class vector arithmetic, anyone?
Now think about the possibilities for natural expression of parallelism in
expressions like {foofunc(), 66, p + q}.

These are *my* impossible dreams for the future of C. Hey, Bjarne -- any
chance you might want to include 1 and 2 above in some future C++ ;-)?
-- 
      Eric S. Raymond                     (the mad mastermind of TMN-Netnews)
      Email: eric@snark.uu.net                       CompuServe: [72037,2306]
      Post: 22 S. Warren Avenue, Malvern, PA 19355      Phone: (215)-296-5718

karl@haddock.ima.isc.com (Karl Heuer) (01/06/89)

(I've added comp.lang.c to the distribution, since a lot of D-designers hang
out there, but I've redirected followups back to comp.lang.misc, since that's
where the discussion seems to belong.  c.l.c readers should subscribe to c.l.m
if they want to continue this thread.)

In article <9310@ihlpb.ATT.COM> nevin1@ihlpb.UUCP (55528-Liber,N.J.) writes:
>In article <2583@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>>I once modified a version of the small-C compiler to make 'C' a completely
>>expression-based language. The changes are really very minor (at least for
>>that case... I don't know quite how full 'C' would take it).
>
>This won't work too well in (full) C.  The problem is that pure
>expression-based languages (like LISP) tend to be typeless as well.
>Having to return a specific data type severely limits the usefulness of making
>control-flow constructs (if, switch, for, etc.) expression-oriented.

Let's try to define EC, an extension of C with the property that every
statement is an expression, yet retaining the property that every expression
has a type.  All constructs with no reasonable value definition become void
expressions.  This includes else-less if, default-less switch, any if- or
switch-statement with branches of incompatible type, and any top-testing loop.
Do-while can be defined to return the value of the last iteration of the loop
body.

The restriction against test-at-top is annoying, but the language must be
prepared to have a value for a loop that executes zero times.  This suggests
that one ought to be able to attach an `else' clause to such a loop.  (I once
had a student who kept trying to do this in Pascal...)  Of course, since
`while (A) B;' is equivalent to `if (A) do B; while (A);', this functionality
is already available by explicitly writing `if (A) {do B; while (A);} else C;',
but a simple `while (A) B; else (C);' would be a more compact way to write it.
(Though it would break C compatibility.)

What about break statements?  We'd probably be better off with a syntax
change, writing `break (EXPR);' to specify the value to be returned by the
enclosing loop or switch.  If we use the current syntax, then in order for a
statement `switch (A) { case 0: B; break; default: C; break; }' to have the
obvious value (A==0?B:C), the rule must be that the value of a switch
statement is that of the statement *preceding* the break.  An analogous rule
could be applied to a break inside a do-while.  (As always, when the rules do
not apply or do not yield a compatible type, the entire expression would be
void-valued for all paths.)

I'm not sure what to do about continue.  If the `else' extension is added,
then a continue which causes the loop to exit (because the retested condition
is now false) could trigger the `else' clause.  (Which would mean that even a
do-while would have use for an else.)  Alternately, a continue could obtain a
value in the same way that a break does, but use it only if the loop test
fails.

Passing values through a goto doesn't seem to work well, since a label by
itself is not a valid statement.  Well, I guess we could say that a labeled
null statement has the value of the statement preceding the goto, but that's
stretching the idea a bit too far, I think.  (I did once suggest this for a
dialect of TECO: I wanted `5 Ofoo$ ... !foo! UX' to assign 5 to register X,
which TECO-10 didn't do.)

(Given that so many statements end up being void-valued expressions, is there
any point to this language extension?  Yes.  There are contexts where a
statement is not legal, but an expression is, even though its value is not
used.)

Of course, a simpler approach would be to add valof...resultis from BCPL, or
inline functions from C++, but it's not really the same as a true expression
language.  On the other hand, a language with expression/statement equivalence
probably shouldn't be based on C in the first place, since several constructs
become redundant (braces vs parens, semicolon vs the comma operator, if-then
vs the ternary operator).

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint