May 09, 2009 assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Consider:
uint fun();
int gun();
...
int[] a = new int[5];
a[fun] = gun;
Which should be evaluated first, fun() or gun()? It's a rather arbitrary decision. C/C++ don't even define an order. Python chooses left-to-right, EXCEPT for assignment, which is right-hand side first. Lisp and C# choose consistent left-to-right. I don't like exceptions and I'd like everything to be left-to-right. However, this leads to some odd cases. Consider this example in TDPL:
import std.stdio, std.string;
void main() {
uint[string] dic;
foreach (line; stdin.byLine) {
string[] words = split(strip(line));
foreach (word; words) {
if (word in dic) continue; // nothing to do
uint newID = dic.length;
dic[word] = newID;
writeln(newID, '\t', word);
}
}
}
If we want to get rid of newID, we'd write:
writeln(dic.length, '\t', word);
dic[word] = dic.length;
by the Python rule, and
writeln(dic.length, '\t', word);
dic[word] = dic.length - 1;
by the C# rule.
What's best?
Andrei
| ||||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu schrieb:
> Consider:
>
> uint fun();
> int gun();
> ....
> int[] a = new int[5];
> a[fun] = gun;
>
> Which should be evaluated first, fun() or gun()? It's a rather arbitrary decision. C/C++ don't even define an order. Python chooses left-to-right, EXCEPT for assignment, which is right-hand side first. Lisp and C# choose consistent left-to-right. I don't like exceptions and I'd like everything to be left-to-right. However, this leads to some odd cases. Consider this example in TDPL:
>
> import std.stdio, std.string;
>
> void main() {
> uint[string] dic;
> foreach (line; stdin.byLine) {
> string[] words = split(strip(line));
> foreach (word; words) {
> if (word in dic) continue; // nothing to do
> uint newID = dic.length;
> dic[word] = newID;
> writeln(newID, '\t', word);
> }
> }
> }
>
> If we want to get rid of newID, we'd write:
>
> writeln(dic.length, '\t', word);
> dic[word] = dic.length;
>
> by the Python rule, and
>
> writeln(dic.length, '\t', word);
> dic[word] = dic.length - 1;
>
> by the C# rule.
>
> What's best?
>
>
> Andrei
From my POV, it would be nice if it would be the same as in Java, because i am porting lots of it to D.
| |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Frank Benoit | Frank Benoit wrote: > From my POV, it would be nice if it would be the same as in Java, > because i am porting lots of it to D. Good point. I searched for that one and found: http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html "The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right." I also searched for the way Perl does it and got a tad disappointed: http://www.nntp.perl.org/group/perl.perl5.porters/2003/09/msg82032.html Essentially the order of evaluation in Perl is as arbitrary and as specific-case-driven as if nobody really sat down and thought any of it. Andrei | |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sat, May 9, 2009 at 1:52 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > I also searched for the way Perl does it and got a tad disappointed: > > http://www.nntp.perl.org/group/perl.perl5.porters/2003/09/msg82032.html > > Essentially the order of evaluation in Perl is as arbitrary and as specific-case-driven as if nobody really sat down and thought any of it. You're surprised by that? ;) | |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu wrote: > Consider: > > uint fun(); > int gun(); > ... > int[] a = new int[5]; > a[fun] = gun; > > Which should be evaluated first, fun() or gun()? It's a rather arbitrary decision. C/C++ don't even define an order. Python chooses left-to-right, EXCEPT for assignment, which is right-hand side first. Lisp and C# choose consistent left-to-right. I don't like exceptions and I'd like everything to be left-to-right. However, this leads to some odd cases. I find this a very interesting issue. I've just coauthored a paper describing three expression evaluation strategies. Their main advantage is that they preserve operator commutativity. In other words, the evaluation order of side-effects is independent from their textual order. No left-to-right or right-to-left. One of the strategies (parallel evaluation) evaluates all subexpressions in the old program state. It maintains operator idempotence. An expression is only legal if no two if its subexpressions make incompatible changes to the state. This strategy requires some extra memory and runtime, at least in its most naive implementation. It does scale extremely well to multi-processor systems, however. Another strategy (ordering by dependency) recognizes read/write and write/write hazards and orders evaluation accordingly. An expression would only be legal if its dependency graph has no cycles. This may be difficult to verify for more complex languages, which have aliasing and such. The third strategy (order agnostic evaluation) basically evaluates all smallest side-effect subexpressions before all pure subexpressions (unless the pure one is a subexpression of a side-effect one). Ordering between side-effects is required to be semantically irrelevant. An expression that depends on any particular ordering of side-effects is illegal. This is non-trivial to prove at compile-time but always possible to test at run-time. And it leaves the compiler free to choose the most efficient evaluation order. Our new programming language Mist will use order agnostic expression evaluation. -- Michiel Helvensteijn | |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:gu4bqu$bq$1@digitalmars.com... > Consider: > > uint fun(); > int gun(); > ... > int[] a = new int[5]; > a[fun] = gun; > > Which should be evaluated first, fun() or gun()? It's a rather arbitrary decision. C/C++ don't even define an order. Python chooses left-to-right, EXCEPT for assignment, which is right-hand side first. Lisp and C# choose consistent left-to-right. I don't like exceptions and I'd like everything to be left-to-right. However, this leads to some odd cases. Consider this example in TDPL: > This may sound strange coming from me, but I think I actually like the Python way of doing it. I may be getting slightly ahead of myself by saying this, but I think the inconsistency lies more with the assignment statement itself than function evaluation order. First of all: lvalue = expression; Anyone with any real experience in imperative programing looks at that and sees "expression (on the right) gets evaluated, and then the result gets placed into lvalue (on the left)". If both sides contain a function call, and the functions in the lvalue get exaluated first, then that would seem to contradict the intuitive understanding of the assignment's natural right-to-left order. I might be venturing slightly offtopic here, but lately, I've been giving a bit of thought to matters of left/right directions. Consider the following: fooA(); fooB(); fooC(); Flow of execution can be followed left-to-right. Which makes sense, because most of the more common (human) languages are left-to-right. But then there's: lvalue = fooC(fooB(fooA(stuff))) Now all of a sudden, it's all completely reversed! If you want to follow this statement's flow-of-execution step-by-step, the entire thing goes right-to-left: First stuff is evaluated, then fooA is called, then fooB is called, then fooC is called, then a value gets assigned to lvalue. Of course, function chaining syntax has been gaining popularity, so in *some* cases you may be able to fix the ordering of the right-hand-side: lvalue = stuff.fooA().fooB().fooC(); (Which, of course, also has the side-benefit of reducing parenthesis-hell). Now, at least the right-hand-side, reads completely in a natural left-to-right order. However, there are languages out there that flip the assignment: auto num; 5 => num; I've never actually liked that in the past. I literally grew up using ApplesoftBASIC and QBASIC, which use the more common "target_var = expression" syntax, so the few times I came across the above, it always felt awkward and uncomfortable. But lately, because of all of this, I've been starting to think "expr => target_var" does have its merits. Of course, the downside is one could argue that "lvalue = fooC(fooB(fooA(stuff)))" reads in order from "general overview" to "more specific", and with "stuff.fooA().fooB().fooC() => target_var;" you have to go all the way to the end to see the general idea of what's finally gotten accomplished. The reason I bring all this up is because that (not that it's likely to actually happen in D) would allow the sensible Python-style of "functions in the expression get evaluated before functions on the assignment-side" *without* making any exceptions to the left-to-right rule (which, as I pointed out before, is really more of an exception with the basic assignment syntax). | |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu schrieb:
> Frank Benoit wrote:
>> From my POV, it would be nice if it would be the same as in Java, because i am porting lots of it to D.
>
> Good point. I searched for that one and found:
>
> http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html
>
> "The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right."
I think, code relying on the order is bad code. And iI think there is no "right" way.
But...
+ it is good to have it defined
+ it is good if ported code will not break because of that difference
I see no other argument. So the question would be, from which language would you expect the most ported code?
I think it will be C/C++ bindings for libs and Java code
For bindings/declarations the evaluation order is not of relevance. So choose Java's scheme. :)
| |||
May 09, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sat, 09 May 2009 11:43:09 -0500, Andrei Alexandrescu wrote: > Consider: > > uint fun(); > int gun(); > ... > int[] a = new int[5]; > a[fun] = gun; > > Which should be evaluated first, fun() or gun()? It's a rather arbitrary decision. C/C++ don't even define an order. Python chooses left-to-right, EXCEPT for assignment, which is right-hand side first. Lisp and C# choose consistent left-to-right. I don't like exceptions and I'd like everything to be left-to-right. However, this leads to some odd cases. Consider this example in TDPL: > > import std.stdio, std.string; > > void main() { > uint[string] dic; > foreach (line; stdin.byLine) { > string[] words = split(strip(line)); > foreach (word; words) { > if (word in dic) continue; // nothing to do > uint newID = dic.length; > dic[word] = newID; > writeln(newID, '\t', word); > } > } > } > > If we want to get rid of newID, we'd write: > > writeln(dic.length, '\t', word); > dic[word] = dic.length; > > by the Python rule, and > > writeln(dic.length, '\t', word); > dic[word] = dic.length - 1; > > by the C# rule. > > What's best? I'm sure about 'best', but I'd prefer the Python method. The example is similar to ... array = array ~ array.length; in as much as the result of the assignment is that the array length changes, but here it more easy to see that the pre-assignment length is being used by the RHS. In COBOL-like syntax ... move dic.length to dic[word]. it is also more obvious what the coder's intentions were. In assembler-like syntax (which is what eventually gets run, of course) ... mov regA, dic.length mov dic[word], regA It just seems counter-intuitive that the target expression's side-effects should influence the source expression. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell | |||
May 11, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu wrote:
> Consider:
>
> uint fun();
> int gun();
> ...
> int[] a = new int[5];
> a[fun] = gun;
>
> Which should be evaluated first, fun() or gun()?
arra[i] = arrb[i++];
arra[i++] = arrb[i];
I'm not sure that such dependences are good code.
By stating a definite order between lvalue and rvalue, you would actually encourage this kind of code.
| |||
May 11, 2009 Re: assignment: left-to-right or right-to-left evaluation? | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Georg Wrede | On 2009-05-11 05:49:01 -0400, Georg Wrede <georg.wrede@iki.fi> said: > Andrei Alexandrescu wrote: >> Consider: >> >> uint fun(); >> int gun(); >> ... >> int[] a = new int[5]; >> a[fun] = gun; >> >> Which should be evaluated first, fun() or gun()? > > arra[i] = arrb[i++]; > > arra[i++] = arrb[i]; > > I'm not sure that such dependences are good code. > > By stating a definite order between lvalue and rvalue, you would actually encourage this kind of code. Well, I agree with you that we shouldn't encourage this kind of code. But leaving it undefined (as in C) isn't a good idea because even if it discourages people from relying on it, it also makes any well tested code potentially buggy when switching compiler. You could simply make it an error in the language to avoid that being written in the first place. But even then you can't catch all the cases statically. For instance, two different pointers or references can alias the same value, as in: int i; func(i, i); void func(ref int i, ref int j) { arra[i++] = arrb[j]; // how can the compiler issue an error for this? } So even if you make it an error for the obvious cases, you still need to define the evaluation order for the ones the compiler can't catch. And, by the way, I don't think we should make it an error even for the so-called obvious cases. Deciding what's obvious and what is not is going to complicate the rules more than necessary. -- Michel Fortin michel.fortin@michelf.com http://michelf.com/ | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply