pierson@mist (Dan Pierson) (12/29/88)
In article <9282@ihlpb.ATT.COM>, nevin1@ihlpb (Liber) writes: >This really has nothing to do with ":=" vs "=". C is an >expression-based language; Pascal is not (by expression-based I mean >that every operator returns a value that can be used as one of the >parameters for another operator). OK, you get the grumble instead of Peter :-) Since you're defining your own term, I can't flatly say you're wrong, BUT this definition of "expression based" contradicts every other definition I'm familiar with. C and Pascal are both statement based languages, however C has a larger set of legal expressions. BLISS and Lisp are both expression based languages, I.E. everything is an expression and returns a value. If C was an expression based language, the ?: expression would be totally redundant with a normal if statement (of course it would still be more compact). If C was an expression based language the following would be legal: foo = switch (bar) { case 'a': ... case 'b': ... default: ... }; This is not fantasy, the equivalent expressions exist and are useful in BLISS and Lisp. -- dan In real life: Dan Pierson, Encore Computer Corporation, Research UUCP: {talcott,linus,necis,decvax}!encore!pierson Internet: pierson@multimax.encore.com
bga@raspail.UUCP (Bruce Albrecht) (12/30/88)
Another expression based language is Algol 68. Not only is a := case month in 31, if year mod 4 = 0 then 29 else 28 fi, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 esac; legal, but so is if a=b then a else c fi := d; and they can be abbreviated to a := (month|31, (year mod 4 = 0|29|28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); and (a=b|a|c) := d; respectively. (And who said C could be terse and unreadable?)
peter@ficc.uu.net (Peter da Silva) (12/30/88)
In article <4505@xenna.Encore.COM>, pierson@mist (Dan Pierson) writes: > In article <9282@ihlpb.ATT.COM>, nevin1@ihlpb (Liber) writes: > >This really has nothing to do with ":=" vs "=". C is an > >expression-based language; Pascal is not (by expression-based I mean > >that every operator returns a value that can be used as one of the > >parameters for another operator). > > OK, you get the grumble instead of Peter :-) Grumble away. > Since you're defining your own term, I can't flatly say you're wrong, > BUT this definition of "expression based" contradicts every other > definition I'm familiar with. You're right. However 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). > If C was an expression based language, the ?: expression would be > totally redundant with a normal if statement (of course it would > still be more compact). The small-C I was working with didn't have '?:', one reason I went to the trouble. The other reason was I'd just read the BCPL book by Colin Whitby-Strevens. While I was making anonymous integer arrays, I went over the edge into algol territory. > If C was an expression based language the following would be legal: [ foo = switch(bar) {...} ] Well, if I'd had a switch statement it would have been. > This is not fantasy, the equivalent expressions exist and are useful > in BLISS and Lisp. And, I believe, Algol. Anyway, it was loads of fun. -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180. `-_-' Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net. 'U` Opinions may not represent the policies of FICC or the Xenix Support group.
nevin1@ihlpb.ATT.COM (Liber) (12/31/88)
In article <2583@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: >In article <4505@xenna.Encore.COM>, pierson@mist (Dan Pierson) 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. >> If C was an expression based language, the ?: expression would be >> totally redundant with a normal if statement (of course it would >> still be more compact). That is why the ?: operator is limited to returning values of the same type (or coersable type), while the if statement is not. >> This is not fantasy, the equivalent expressions exist and are useful >> in BLISS and Lisp. I agree. Side note: In LISP, I tend to think of the side-effect of an expression as what it does, while its main purpose is to return a value (eg: the side effect of setq is to assign a value to a variable, while its main effect is to return its value or the variable; I can't remember which). In Icon, I tend to reverse these definitions. Is it just me, or is there something about the different paradigms that switch these definitions? Happy New Year! -- NEVIN ":-)" LIBER AT&T Bell Laboratories nevin1@ihlpb.ATT.COM (312) 979-4751
rej@ukc.ac.uk (R.E.Jones) (01/03/89)
In article <9310@ihlpb.ATT.COM> nevin1@ihlpb.UUCP (55528-Liber,N.J.) writes: >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. Modern functional languages tend to be strongly typed. >Having to return a specific data type severely limits the usefulness of making >control-flow constructs (if, switch, for, etc.) expression-oriented. Quite right ... IF you have to return a specific data type. However, modern functional languages like Miranda, employ a polymorphic type discipline. One therefore gets all the benefits of more powerful combining forms without having to rewrite essentially the same code for each specific data type. Richard Jones.
miller@lll-crg.llnl.gov (Patrick Miller) (01/04/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. he 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. Strongly typed functional languages like SISAL support this very concept. Constructs like: IF (test 1) THEN exp 1 ELSIF (test 2) THEN exp 2 ELSE exp 3 ENDIF are required where exp 1, exp 2, exp 3 are all of the same type. This is not as restrictive as one might think. Patrick Miller Patrick J. Miller miller@lll-crg.llnl.gov uucp: {gatech,pyramid,rutgers}!lll-crg!miller other things to try: miller%lll-crg.llnl.gov@relay.cs.net
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
w-colinp@microsoft.UUCP (Colin Plumb) (01/06/89)
Actually, defining the semantics of such things is easy. A friend of mine did it in a language design where {} created first-class procedures. I.e. you didn't call while(cond) { body... } you called while ({cond}, { body }) and while was defined as (in a C-like syntax, where "int foo()" means a *procedure* not a pointer to one): void while(bool test(), void body()) { if (test()) { body(); while(test, body); } } which, was, of course, really written more like void while (bool test(), void body)) = { ifthen(test, {body; while(test, body)}) } There was, of course, syntactic sugar to make this more usable, but while and relatives were in fact not part of the language, merely part of the standard library. -- -Colin (uunet!microsof!w-colinp)
peter@ficc.uu.net (Peter da Silva) (01/07/89)
Three comments: First, a = for(s1;e;s2) Is this legal? If e fails, is the value s1? also, [BCPL resultis...] This is equivalent to your 'break(n)', above. I like this very much. I didn't do anything like 'resultis', and had break return the value of the preceding expression. and finally, For expressions like if-without-else, the value if no statement was executed was zero, NULL, whatever. This was in analogy to short-circuit AND (which wasn't in the language). That is, "a = if(b) then 2" was equivalent to "a = b || 2". This also obviated the need for flow analysis, and let me just use the value in BC as the result of a statement :->. ... OK. I think the name "E" is better than "EC". (I called mine "Small-P :->"). How about making break(n) work in any block? Hmmm. No. Hmmm. Maybe resultis would be better. Simplify it, make it 'result'. I think the value of a statement with varying types is soluble by looking at the coercion needed to use it in context. If function prototypes are mandated, the only problem is printf(). And I think the promotion rules will work here. -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180. `-_-' Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net. 'U` Opinions may not represent the policies of FICC or the Xenix Support group.
anw@nott-cs.UUCP (01/09/89)
In article <11359@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes: [a thoughtful and interesting article about how to extend C into a full 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). On the third hand [:-),*], this redundancy can be very helpful to humans. Eg, if parens/braces/brackets were everywhere interchangeable, then a measure of "elegant variation" makes multi-bracketed expressions more readable and may help the compiler to produce better error messages (by localising bracket mis-matches). Similarly, having both "if-then" and "?:" gives the human some syntactic sugar with which to clarify meaning. ------- * On a clock, the third hand is presumably the second hand. -- Andy Walker, Maths Dept., Nott'm Univ., UK. anw@maths.nott.ac.uk
karl@haddock.ima.isc.com (Karl Heuer) (01/12/89)
In article <2659@ficc.uu.net> you write: >Three comments: First, > > a = for(s1;e;s2) > > Is this legal? If e fails, is the value s1? I hadn't thought about that, but it's a reasonable definition -- provided the type of s1 matches the type of the body of the for statement. It does require saving the value of s1 before executing e, which is somewhat couterintuitive. > For expressions like if-without-else, the value if no statement > was executed was zero, NULL, whatever. This was in analogy to > short-circuit AND (which wasn't in the language). This would be self-consistent, but I'm not sure how useful it would be. In a while-statement, for example, if the value at the bottom of the loop is some useful value, the default (zero-execution) value would probably be some sort of error indication. Zero is not always an out-of-band value. On the other hand, a void value is even less useful, and the existence of else (for if-statements), default (for switch-statements), and the above trick of using `for' instead of `while' means that the user has complete control over the default value in any case. Given this, it seems acceptable to use zero as the `default default', when no explicit default is specified. It's also within the spirit of C, I think. >I think the value of a statement with varying types is soluble by looking >at the coercion needed to use it in context. Context outside of the expression itself? I think that's dangerous ground. Can you give an example of how one might use this? Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
peter@ficc.uu.net (Peter da Silva) (01/14/89)
In article <2659@ficc.uu.net> I write: > >Three comments: First, > > a = for(s1;e;s2) > > Is this legal? If e fails, is the value s1? In article <11398@haddock.ima.isc.com>, karl@haddock.ima.isc.com (Karl Heuer) writes: > I hadn't thought about that, but it's a reasonable definition -- provided the > type of s1 matches the type of the body of the for statement. I don't see why. After all, if you have an expression in current 'C' like: a = b ? c : d; The types of c and d don't have to match. Why add an additional constraint in the extended language? > It does require > saving the value of s1 before executing e, which is somewhat couterintuitive. True. This might be a bit of a problem. I wonder if there's any way around that... > >I think the value of a statement with varying types is soluble by looking > >at the coercion needed to use it in context. > Context outside of the expression itself? I think that's dangerous ground. That's the way it currently works for ?:. > Can you give an example of how one might use this? int a; a = switch(nextchar()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': result 'O'; case '8': case '9': result 'D': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': result 'X'; default: result -1; } -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180. `-_-' Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net. 'U` Opinions may not represent the policies of FICC or the Xenix Support group.
chris@mimsy.UUCP (Chris Torek) (01/15/89)
[re type-matching in multipart expression values] >>Context outside of the expression itself? I think that's dangerous ground. In article <2734@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: >That's the way it currently works for ?: [in C]. No: in `a ? b : c', the type of the result is based only on the types of `b' and `c', much as though b and c were to be added. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
peter@ficc.uu.net (Peter da Silva) (01/16/89)
In article <15472@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes: > [re type-matching in multipart expression values] > >>Context outside of the expression itself? I think that's dangerous ground. > In article <2734@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes: > >That's the way it currently works for ?: [in C]. > No: in `a ? b : c', the type of the result is based only on the > types of `b' and `c', much as though b and c were to be added. OK, the bottom line for expression-C remains the same... there is no reason you shouldn't be able to say: a = if (this) then 10.0 else 'a'; ...and have all the coercions work the same way they do in real-C for: a = (this) ? 10.0 : 'a'; Personally I wish that this sort of implicit type-coercions in 'C' was be deferred as late as possible, to take advantage of surrounding context. That way you wouldn't be burned by assuming this makes sense: long_var = (unsigned short)65535 + (signed short)32767; This calculation seems, to the 'C' novice, like it should equal 98302, but it actually ends up 32766. -- Peter da Silva, Xenix Support, Ferranti International Controls Corporation. Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180. `-_-' Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net. 'U` Opinions may not represent the policies of FICC or the Xenix Support group.
karl@haddock.ima.isc.com (Karl Heuer) (01/17/89)
>>> a = for(s1;e;s2) >>> Is this legal? If e fails, is the value s1? > >>[reasonable, but s1 and the for-body would have to have matching types.] > >I don't see why. I meant that they have to be compatible in the same sense as the ?: operands, i.e. it must be possible to bring them to a common type. It would not be legal to write a = for(int_expr; e; s2) string_expr; >>>I think the value of a statement with varying types is soluble by looking >>>at the coercion needed to use it in context. > >>Context outside of the expression itself? I think that's dangerous ground. > >That's the way it currently works for ?:. We must be talking about different things. I agree that it is possible to assign a type to any statement whose component types are sufficiently compatible, in the same way that ?: does%. I believe this is what I said in my first posting on the subject, in fact. In the case of incompatible types, such as the example above, I think we'd be treading on dangerous ground if we tried to resolve it to anything more meaningful than a void expression. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint ________ % As Chris notes, this does not involve any context beyond the expression being typed. In particular, on a 16-bit implementation, ulongvar = ushortvar * ushortvar does a 16-bit multiply, not a 32-bit multiply; this is a classic gotcha.