www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [Suggestion] DeclarationStatement as Expression.

reply AJG <AJG_member pathlink.com> writes:
Hi there,

What do you think? The idea is for the statement to inherit the scope of the
construct or block it is declared in. Similar to how a for loop does it:

# for (int i = 0; i < length; ++i) Foo(i);

In addition, the expression inherits the type and value of the declaration
itself. For instance:

# if ((string line = ReadLine()) != null) {
#      printf("We got a line, Chief!");
#      line = EscapeLine(line);
#      DeployLine(line);
# } else {
#      printf("We are out of lines, Chief!");
#      line = "Emergency Line";
#      DeployLine(line);
# }

I see it as a simple extension of what's already available with assignment
expressions. Now, would this be possible? What are the disadvantages?
I don't think this would break any code.

Cheers,
--AJG.
Aug 09 2005
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 10 Aug 2005 02:44:01 +0000 (UTC), AJG wrote:

 Hi there,
 
 What do you think? The idea is for the statement to inherit the scope of the
 construct or block it is declared in. Similar to how a for loop does it:
 
 # for (int i = 0; i < length; ++i) Foo(i);
 
 In addition, the expression inherits the type and value of the declaration
 itself. For instance:
 
 # if ((string line = ReadLine()) != null) {
 #      printf("We got a line, Chief!");
 #      line = EscapeLine(line);
 #      DeployLine(line);
 # } else {
 #      printf("We are out of lines, Chief!");
 #      line = "Emergency Line";
 #      DeployLine(line);
 # }
 
 I see it as a simple extension of what's already available with assignment
 expressions. Now, would this be possible? What are the disadvantages?
 I don't think this would break any code.
 

I remember this being suggested so time ago. It could be a nice addition. In the meantime, will this do ... ? { string line; if ((line = ReadLine()) != null) { printf("We got a line, Chief!"); line = EscapeLine(line); DeployLine(line); } else { printf("We are out of lines, Chief!"); line = "Emergency Line"; DeployLine(line); } } This limits the scope of the declaration. -- Derek (skype: derek.j.parnell) Melbourne, Australia 10/08/2005 12:56:09 PM
Aug 09 2005
parent AJG <AJG_member pathlink.com> writes:
I remember this being suggested so time ago. It could be a nice addition.

Yay! First supporter.
In the meantime, will this do ... ?

{ string line;
if ((line = ReadLine()) != null) {
     printf("We got a line, Chief!");
     line = EscapeLine(line);
     DeployLine(line);
} else {
     printf("We are out of lines, Chief!");
     line = "Emergency Line";
     DeployLine(line);
}
}

This limits the scope of the declaration.

The scope limitation is not the main benefit to me, but rather that the whole thing becomes nicely streamlined. Oh, and the other neg. side effect is Indentation++. You kinda cheated. ;) Cheers, --AJG.
Aug 09 2005
prev sibling next sibling parent AJG <AJG_member pathlink.com> writes:
Hi there,

I forgot to add something.
If the statement/expression contains multiple declarations, like:
int i = 1, j = 2, k = 3;

Then my suggestion would work just like the Comma operator currently does. It
makes the expression equal to the very last declaration.

# // Currently allowed:
# int foo = (1, 2, 3);
# // foo == 3.

# // My suggestion would allow:
# int foo = (int i = 1, j = 2, k = 3);
# // foo == 3.

Cheers,
--AJG.
Aug 09 2005
prev sibling next sibling parent Chris Sauls <ibisbasenji gmail.com> writes:
AJG wrote:
 Hi there,
 
 What do you think?

I think it could be useful. Especially if it could be made to work with 'while()' statements and their ilk. Perhaps via an implied 'static' attribute. -- Chris Sauls
Aug 09 2005
prev sibling next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
AJG wrote:
 Hi there,
 
 What do you think? The idea is for the statement to inherit the scope of the
 construct or block it is declared in. Similar to how a for loop does it:
 
 # for (int i = 0; i < length; ++i) Foo(i);
 
 In addition, the expression inherits the type and value of the declaration
 itself. For instance:
 
 # if ((string line = ReadLine()) != null) {
 #      printf("We got a line, Chief!");
 #      line = EscapeLine(line);
 #      DeployLine(line);
 # } else {
 #      printf("We are out of lines, Chief!");
 #      line = "Emergency Line";
 #      DeployLine(line);
 # }
 
 I see it as a simple extension of what's already available with assignment
 expressions. Now, would this be possible? What are the disadvantages?
 I don't think this would break any code.
 
 Cheers,
 --AJG.
 

I take it this would also allow multiple definitions in the Initializer part of a ForStatement? I.e.: for (int i = 0, int j = 2;;) If so, that's another plus, at least for me.
Aug 10 2005
parent AJG <AJG_member pathlink.com> writes:
Hi,

I take it this would also allow multiple definitions in the Initializer
part of a ForStatement? I.e.:

for (int i = 0, int j = 2;;)

Yep.
If so, that's another plus, at least for me.

That would be nice indeed. Cheers, --AJG.
Aug 10 2005
prev sibling next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:ddbplh$17c3$1 digitaldaemon.com...
 Hi there,

 What do you think? The idea is for the statement to inherit the scope of 
 the
 construct or block it is declared in. Similar to how a for loop does it:

 # for (int i = 0; i < length; ++i) Foo(i);

 In addition, the expression inherits the type and value of the declaration
 itself. For instance:

 # if ((string line = ReadLine()) != null) {
 #      printf("We got a line, Chief!");
 #      line = EscapeLine(line);
 #      DeployLine(line);
 # } else {
 #      printf("We are out of lines, Chief!");
 #      line = "Emergency Line";
 #      DeployLine(line);
 # }

 I see it as a simple extension of what's already available with assignment
 expressions. Now, would this be possible? What are the disadvantages?
 I don't think this would break any code.

 Cheers,
 --AJG.

are the following valid? if (bar(x,int x = 1)) if (0 || int x = 1) In other words, what exactly are the semantics of the proposal?
Aug 10 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

are the following valid?
if (bar(x,int x = 1))
if (0 || int x = 1)

In theory, yes. It's possible that due to some ambiguity the parsing may require an extra set of parentheses: # if (bar(x, (int x = 1))) # if (0 || (int x = 1)) Also, I'm not sure if the two x vars would conflict. The docs say they _would_, but the compiler says they wouldn't. That would be up for discussion.
In other words, what exactly are the semantics of the proposal? 

Well, the premise is that there are many places where an inline declaration makes sense and is very convenient. The for loop is such an example. My idea is to extend this. Of course, your examples are trivial, but consider something like: # if (bar(42, int x = rand())) foo(x); Or something like: # if (0 || (int x = SideEffect()) > 5 && x < 10) bar(x); Anyway, this concept is not radical. It can already be done in PHP, Perl, and various other languages. I think it would be fantastic to bring it over to our corner of the world with the added benefits of strong typing. Cheers, --AJG.
Aug 10 2005
next sibling parent reply xs0 <xs0 xs0.com> writes:
Well, Walter already said he likes the idea:

http://www.digitalmars.com/d/archives/digitalmars/D/20481.html

Hopefully that means it will get implemented eventually :) BTW, when 
would you need this outside if/for/while? Using local declarations in 
those has the benefit of scoping them locally to the loop, while using 
them randomly in normal statements just makes the code harder to read, 
imho..


Cheers,
xs0

AJG wrote:
 Hi,
 
 
are the following valid?
if (bar(x,int x = 1))
if (0 || int x = 1)

In theory, yes. It's possible that due to some ambiguity the parsing may require an extra set of parentheses: # if (bar(x, (int x = 1))) # if (0 || (int x = 1)) Also, I'm not sure if the two x vars would conflict. The docs say they _would_, but the compiler says they wouldn't. That would be up for discussion.
In other words, what exactly are the semantics of the proposal? 

Well, the premise is that there are many places where an inline declaration makes sense and is very convenient. The for loop is such an example. My idea is to extend this. Of course, your examples are trivial, but consider something like: # if (bar(42, int x = rand())) foo(x); Or something like: # if (0 || (int x = SideEffect()) > 5 && x < 10) bar(x); Anyway, this concept is not radical. It can already be done in PHP, Perl, and various other languages. I think it would be fantastic to bring it over to our corner of the world with the added benefits of strong typing. Cheers, --AJG.

Aug 10 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

Well, Walter already said he likes the idea:
http://www.digitalmars.com/d/archives/digitalmars/D/20481.html

Glad to hear that!
Hopefully that means it will get implemented eventually :)

Amen to that. Hopefully soon, too.
BTW, when 
would you need this outside if/for/while? Using local declarations in 
those has the benefit of scoping them locally to the loop, while using 
them randomly in normal statements just makes the code harder to read, 
imho..

I will assume by if/for/while you also meant switch/foreach/etc. right? Anyway, outside of these constructs the feature can also be useful. Quick, trivial example: # string line = (string temp = readline()) ? escape(temp) : null; But this is besides the point. The main issue to consider here is orthogonality. Let me give you an analogy: Back around the time of the civil war, the declaration of local vars in C had to occur at the very beginning of the function. # void Foo() { # printf("Foo"); # int Bar = 5; // ERROR. # } Then, Congress legislated heavily against this tyranny and allowed declarations everywhere. The people rejoiced. Peace was declared. You could make the argument that this is "harder to read," but that's baloney IMHO. What this is hiding is that declarations were not orthogonal. They didn't work everywhere, and this was a severe limitation. In that vein, DeclarationExpressions should work everywhere, not only in certain, special constructs. That's the best way to proceed, and it guarantees the most flexibility. Wouldn't you agree? --AJG.
Aug 11 2005
parent reply xs0 <xs0 xs0.com> writes:
Hi,

AJG wrote:
Hopefully that means it will get implemented eventually :)

Amen to that. Hopefully soon, too.

Hopefully :)
BTW, when 
would you need this outside if/for/while? Using local declarations in 
those has the benefit of scoping them locally to the loop, while using 
them randomly in normal statements just makes the code harder to read, 
imho..

I will assume by if/for/while you also meant switch/foreach/etc. right?

Yup, any statement that has a sub-statement..
 Anyway, outside of these constructs the feature can also be useful. 
 
 Quick, trivial example:
 # string line = (string temp = readline()) ? escape(temp) : null;

Well, before we get into beauty, would temp be a valid variable after this statement as well, or would you have it be local to the statement? (imho, it should be local).
 But this is besides the point. The main issue to consider here is
orthogonality.
 Let me give you an analogy:
 
 Back around the time of the civil war, the declaration of local vars in C had
to
 occur at the very beginning of the function.
 
 # void Foo() {
 #    printf("Foo");
 #    int Bar = 5; // ERROR.
 # }
 
 Then, Congress legislated heavily against this tyranny and allowed declarations
 everywhere. The people rejoiced. Peace was declared.
 
 You could make the argument that this is "harder to read," but that's baloney
 IMHO. What this is hiding is that declarations were not orthogonal. They didn't
 work everywhere, and this was a severe limitation.
 
 In that vein, DeclarationExpressions should work everywhere, not only in
 certain, special constructs. That's the best way to proceed, and it guarantees
 the most flexibility.
 
 Wouldn't you agree?

Well, I'm not sure I agree completely.. I think that DeclarationExpressions, as you call them, reduce legibility of code, so wherever allowed, they should provide some benefit.. In the civil war case above, the benefit is that you're able to declare a variable near to the place where it's used (improving legibility), and also inside non-top-level blocks of code (ditto). Introducing a variable inside the expression of while() et al. has the benefit of making it local to the statement/block, which otherwise requires some ugliness (i.e. creating a new {} block). I mean, the main reason I want this change is so that as many vars as possible can be scoped as locally as possible without requiring superfluous blocks. I don't mind declaring the variables outside (but near) the statement that uses them, but I do care whether it's valid beyond that statement - if it's a stamenent-local var, it should be valid only inside the stmt.. The only common cases where that is not easily achievable is inside the expressions of if(), while(), switch(), etc.. If we look at your example of escape(readline()), its effect on the code that follows is exactly the same as if it was string line=readline(); if (line) line=escape(line); which indicates structure much better (escape is called conditionally) and it's even less characters to type :) OTOH, below there is a difference - in the first case bar remains declared after the while(), while in the second it doesn't. int bar; while (bar=foo()) { ... } vs. while (int bar=foo()) { ... } xs0
Aug 11 2005
parent AJG <AJG_member pathlink.com> writes:
Hi,

 I will assume by if/for/while you also meant switch/foreach/etc. right?


Ok.
 Quick, trivial example:
 # string line = (string temp = readline()) ? escape(temp) : null;

Well, before we get into beauty, would temp be a valid variable after this statement as well, or would you have it be local to the statement? (imho, it should be local).

Yes, it would be invalid after the statement (thus "local").
 In that vein, DeclarationExpressions should work everywhere, not only in
 certain, special constructs. That's the best way to proceed, and it guarantees
 the most flexibility.
 Wouldn't you agree?

Well, I'm not sure I agree completely.. I think that DeclarationExpressions, as you call them, reduce legibility of code, so wherever allowed, they should provide some benefit.. In the civil war case above, the benefit is that you're able to declare a variable near to the place where it's used (improving legibility), and also inside non-top-level blocks of code (ditto). Introducing a variable inside the expression of while() et al. has the benefit of making it local to the statement/block, which otherwise requires some ugliness (i.e. creating a new {} block).

IMO the main improvement in the civil war case was the introduction of orthogonality. This alone lead way to flexibility and convenience. In turn, the better legibility came as a byproduct.
I mean, the main reason I want this change is so that as many vars as 
possible can be scoped as locally as possible without requiring 
superfluous blocks. I don't mind declaring the variables outside (but 
near) the statement that uses them, but I do care whether it's valid 
beyond that statement - if it's a stamenent-local var, it should be 
valid only inside the stmt.. The only common cases where that is not 
easily achievable is inside the expressions of if(), while(), switch(), 
etc..

If we look at your example of escape(readline()), 
its effect on the code 
that follows is exactly the same as if it was

string line=readline(); 
if (line)
    line=escape(line);

Yes. That seems right.
which indicates structure much better (escape is called conditionally) 
and it's even less characters to type :) 

Not necessarily. Your version is 2 extra lines. This may seem trivial but that's actually 300% the vertical screen space of the original. To me, shorter is better. Almost invariably. If you can use this to reduce lines throughout, then you are looking at a good potential for less code. I _love_ the ternary operator because it allows me to shorten ifs and pack more into less. I have a feeling it's not your favorite operator ;)
OTOH, below there is a 
difference - in the first case bar remains declared after the while(), 
while in the second it doesn't.

int bar;
while (bar=foo()) { ... }

vs.

while (int bar=foo()) { ... }

The same thing can be applied to regular DeclarationExpressions. [From another post]: # string result = # (string s = readline()) ? # (string t = readline() ? # processTwo(s, t) : # processOne(s)) : # processNone(); Is more or less equivalent to: # string result = null; # # { # string s = null, t = null; # # result = # (s = readline()) ? # (t = readline() ? # processTwo(s, t) : # processOne(s)) : # processNone(); # } So s and t have the advantage of tight local scoping just as well. It also helps remove those "superfluous blocks" you mentioned. In addition, it allows you to streamline temps right out of the way. That's one of my points from what I said before about packing more into less. # string s = null, t = null; and # string result = null; Are total fluff. They're unneeded. It's _just_ like taking the civil war case further by allowing declarations to be even more flexible. Remember, legibility can come as the byproduct of this extra flexibility and convenience. Sure, I understand that also _less_ readable things could be done with it. This is a valid point. But, unreadable code can be written regardless. I don't think this promotes writing such code. I think, however, that the gains are much better than that this small potential downside, which is negligible IMO. Obfuscated code is very easy to write, in very many languages, with very few features needed. Cheers, --AJG.
Aug 11 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:dddoj5$ucv$1 digitaldaemon.com...
 Hi,

are the following valid?
if (bar(x,int x = 1))
if (0 || int x = 1)

In theory, yes. It's possible that due to some ambiguity the parsing may require an extra set of parentheses: # if (bar(x, (int x = 1))) # if (0 || (int x = 1))

I wasn't getting at parsing issues. I was trying to get at semantics. More specifically, 1) can the declared variable be used in the expression before the declaration appears and 2) if the initializer for the variable is never evaluated is the declaration still valid and what is the initial value (I assumed this one was the type's init property since that should always be available).
 Also, I'm not sure if the two x vars would conflict. The docs say they 
 _would_,
 but the compiler says they wouldn't. That would be up for discussion.

In other words, what exactly are the semantics of the proposal?

Well, the premise is that there are many places where an inline declaration makes sense and is very convenient. The for loop is such an example. My idea is to extend this.

Sure. I don't have a problem with the general motivations. I'm asking for details.
 Of course, your examples are trivial, but consider something like:

 # if (bar(42, int x = rand())) foo(x);

I don't see a problem with this one. can you explain the issue? Is it that the declaration appears as a parameter to bar?
 Or something like:

 # if (0 || (int x = SideEffect()) > 5 && x < 10) bar(x);

Now you're gettin' my point...
 Anyway, this concept is not radical. It can already be done in PHP, Perl, 
 and
 various other languages. I think it would be fantastic to bring it over to 
 our
 corner of the world with the added benefits of strong typing.

 Cheers,
 --AJG.

I was trying to point out the complications in allowing the declaration to appear just anywhere in an expression (ie short-circuiting and order-of-evaluation issues). It isn't obvious what is legal and what isn't.
Aug 10 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

I wasn't getting at parsing issues. I was trying to get at semantics. More 
specifically, 1) can the declared variable be used in the expression before 
the declaration appears and 2) if the initializer for the variable is never 
evaluated is the declaration still valid and what is the initial value (I 
assumed this one was the type's init property since that should always be 
available).

Oh, I totally misunderstood that. I thought x was already declared, and you wanted to know if a newly declared x would be legal. # if (bar(x, (int x = 1))) I see the issue now. This would be a lot easier if parameters were guaranteed left-to-right. Btw, is there actually any chance this will ever happen? At any rate, as it stands (no guarantee), it could work like this: # if (bar(x, (int x = 1))) // illegal. # if (bar(int x = 1, x)) // legal So, declarations follow left-to-right. So you _can_ refer to a variable declared in a previous parameter (because it's in scope), but it's not guaranteed to have been initialized. This would seem consistent with how it is right now. In other words, it's up to the programmer not to do it. This is just like: # if (bar(i++, i, ++i)) Which is legal, but unsafe. Would that make sense? Seems like the simplest way to me.
Sure. I don't have a problem with the general motivations. I'm asking for 
details.

Any other questions I on your mind ;) ? I'm actually glad you brought this up. I hadn't thought about those pesky function parameter irregularities.
 Of course, your examples are trivial, but consider something like:

 # if (bar(42, int x = rand())) foo(x);

I don't see a problem with this one. can you explain the issue? Is it that the declaration appears as a parameter to bar?

Well, now I'm confused. Should there be an issue? I was just pointing out an example of when inline declarations are useful.
 Or something like:

 # if (0 || (int x = SideEffect()) > 5 && x < 10) bar(x);


I don't see an issue here either. And/Or have guaranteed orders of execution. My point was to show how to use the technique to avoid calling a side-effect-prone function more than once without using outside temps. So, for example, this is illegal: # if (I && int I = 0) // First I is undeclared. So is this: # if (int I = 0 && int I = 0) // Second I is re-definition in same scope. So is this: # if (int I = 0) int I = 0; // Second I is re-definition in same scope.
I was trying to point out the complications in allowing the declaration to 
appear just anywhere in an expression (ie short-circuiting and 
order-of-evaluation issues). It isn't obvious what is legal and what isn't. 

Yes. I totally missed this point in my last post. Anyway, the only issue I see is for when order of execution is not guaranteed. As I mentioned, in this case declarations would enter scope left-to-right as usual, but evaluation of the expression is not guaranteed because of the underlying mechanism. Btw: Is there any other place where order of execution isn't guaranteed? Cheers, --AJG.
Aug 10 2005
next sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 Of course, your examples are trivial, but consider something like:

 # if (bar(42, int x = rand())) foo(x);

I don't see a problem with this one. can you explain the issue? Is it that the declaration appears as a parameter to bar?

Well, now I'm confused. Should there be an issue? I was just pointing out an example of when inline declarations are useful.

Oh, I thought you were looking for troublesome corner cases like I was. I have no problem with motivation.
 Or something like:

 # if (0 || (int x = SideEffect()) > 5 && x < 10) bar(x);


I don't see an issue here either. And/Or have guaranteed orders of execution.

My bad. I meant && instead of ||. Otherwise the 0 || is silly.
Aug 10 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
Sure. I don't have a problem with the general motivations. I'm asking for
details.

Any other questions I on your mind ;) ? I'm actually glad you brought this up. I hadn't thought about those pesky function parameter irregularities.

Here's the answer I was hoping for: Inside if, while and do-while conditions "if (cond) stmt else stmt" if a declaration "decl" (without initializer) appears in cond then it is equivalent to {decl;if(cond')stmt else stmt} where cond' is the condition without the declaration. That implies the variable is still valid with it's default initial value if the declaration is never "evaluated".
Aug 10 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

Inside if, while and do-while conditions "if (cond) stmt else stmt" if a 
declaration "decl" (without initializer) appears in cond then it is 
equivalent to
  {decl;if(cond')stmt else stmt}
where cond' is the condition without the declaration. That implies the 
variable is still valid with it's default initial value if the declaration 
is never "evaluated".

I'm not sure I understand you correctly, but if I do, then the same thing could be applied to parameters: # bar(int i = 5, i); Becomes equivalent to: # { int i; bar(i = 5, i); } Which in turn is equivalent to # bar(5, 0); // with r-t-l. # bar(5, 5); // with l-t-r. Right? Thinking about this a little, I think it would be a good idea to prevent parameters from refering to other declarations from previous parameters. The results are undefined anyway. Wouldn't you say? # bar(int i = 5, i); // make this illegal. # bar(i, int i = 5); // this would be illegal too. # bar(int i = 5, 42); // this is ok. # bar(42, int i = 5); // this is ok. --------------- As for declarations within if/while/for/switch/etc parentheses, then what you proposed sounds good: # if (int i = 3) i = 4; # while (string s = readline()) processline(s); # switch (char c = getchar()) { case 'q': quit(c); } are equivalent to: # { int i; if (i = 3) i = 4; } # { string s; while (s = readline()) processline(s); } # { char c; switch (c = getchar()) { case 'q': quit(c); } And so on. Outside of these constructs, it also works: # int i = int j = 4; is equivalent to: # { int j, i = j = 4; } and # string result = # (string s = readline()) ? # (string t = readline() ? # processTwo(s, t) : # processOne(s)) : # processNone(); to: # { string s, t, result = # (s = readline()) ? # (t = readline() ? # processTwo(s, t) : # processOne(s)) : # processNone(); } Cheers, --AJG.
Aug 10 2005
parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:dded04$1poo$1 digitaldaemon.com...
 Hi,

Inside if, while and do-while conditions "if (cond) stmt else stmt" if a
declaration "decl" (without initializer) appears in cond then it is
equivalent to
  {decl;if(cond')stmt else stmt}
where cond' is the condition without the declaration. That implies the
variable is still valid with it's default initial value if the declaration
is never "evaluated".

I'm not sure I understand you correctly, but if I do, then the same thing could be applied to parameters: # bar(int i = 5, i); Becomes equivalent to: # { int i; bar(i = 5, i); } Which in turn is equivalent to # bar(5, 0); // with r-t-l. # bar(5, 5); // with l-t-r. Right? Thinking about this a little, I think it would be a good idea to prevent parameters from refering to other declarations from previous parameters. The results are undefined anyway. Wouldn't you say? # bar(int i = 5, i); // make this illegal. # bar(i, int i = 5); // this would be illegal too. # bar(int i = 5, 42); // this is ok. # bar(42, int i = 5); // this is ok.

Makes sense to me. I was also worried about the parsing algorithm for expressions - if it finds a symbol that it doesn't know the type of it has to go look though the whole rest of the expression to find the declaration. That sounds like it would confuse compilers and people. So I'm liking the notion that if the parser finds a symbol that it doesn't know the type of (without rewriting the declarations) that it's an error. So a symbol that is declared in the expression can only be used once - namely in that declaration. If the expression never evaluates that subexpression then the symbol is still valid but has its default initial value.
 ---------------

 As for declarations within if/while/for/switch/etc parentheses, then what 
 you
 proposed sounds good:

 # if (int i = 3) i = 4;
 # while (string s = readline()) processline(s);
 # switch (char c = getchar()) { case 'q': quit(c); }

 are equivalent to:

 # { int i; if (i = 3) i = 4; }
 # { string s; while (s = readline()) processline(s); }
 # { char c; switch (c = getchar()) { case 'q': quit(c); }

 And so on. Outside of these constructs, it also works:

 # int i = int j = 4;

 is equivalent to:

 # { int j, i = j = 4; }

 and

 # string result =
 #     (string s = readline()) ?
 #         (string t = readline() ?
 #             processTwo(s, t) :
 #             processOne(s)) :
 #         processNone();

 to:

 # { string s, t, result =
 #     (s = readline()) ?
 #         (t = readline() ?
 #             processTwo(s, t) :
 #             processOne(s)) :
 #         processNone(); }

 Cheers,
 --AJG.

I don't know if I'm completely sold on the use of "inline" declarations outside conditions of if/while/etc. In particular I'd be tempted to say in the readline example above the use of s and t outside of the declaration within the same expression statement should be an error (for the same reasons as above). And if that's true then the usefulness of those use cases in near 0 since the symbol goes out of "scope" at the end of the statement. So it would really only be useful in conditions - which is the case that people care about anyway.
Aug 10 2005
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
AJG wrote:

 Hi there,
 
 What do you think? The idea is for the statement to inherit the scope of the
 construct or block it is declared in. Similar to how a for loop does it:
 
 # for (int i = 0; i < length; ++i) Foo(i);
 
 In addition, the expression inherits the type and value of the declaration
 itself. For instance:
 
 # if ((string line = ReadLine()) != null) {
 #      printf("We got a line, Chief!");
 #      line = EscapeLine(line);
 #      DeployLine(line);
 # } else {
 #      printf("We are out of lines, Chief!");
 #      line = "Emergency Line";
 #      DeployLine(line);
 # }
 
 I see it as a simple extension of what's already available with assignment
 expressions. Now, would this be possible? What are the disadvantages?
 I don't think this would break any code.

There's a possible difficulty here if ((qwert * yuiop = asdfg) != 0) Is this a multiplication or a declaration of a pointer? At the moment, this is syntactically a multiplication. Though not semantically valid. We could apply the "if it's parseable as a declaration then it's a declaration" rule here, but this would also break such semantically valid code as if (qwert * yuiop == 0) .... Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Aug 11 2005
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 There's a possible difficulty here

     if ((qwert * yuiop = asdfg) != 0)

     if (qwert * yuiop == 0)

Good catch. I agree those examples are a problem. The rules for this feature should be very simple and unambiguous or else it isn't worth it IMHO. Personally I'm starting to think there are too many potentially confusing situations with parsing and semantics but a way out is to model "for" loops more closely: separate the declarations from the expressions. Something like if (qwert* yuiop=asdfg; yuiop != 0) in which case it's starting to look pretty similar to how one would write it today. In other words the code if (decl1, decl2, ... declN ; expr) stmt else stmt is the same as {decl1; decl2; declN; if (expr) stmt else stmt} Actually the "if (type var = init; var != 0)" style is arguably more readable than the idiom "if ((var = init) != 0)" though one could continue to use that idiom "if (type var; (var = init) != 0)"
Aug 11 2005
parent AJG <AJG_member pathlink.com> writes:
Hi,

 There's a possible difficulty here

     if ((qwert * yuiop = asdfg) != 0)

     if (qwert * yuiop == 0)

Good catch. I agree those examples are a problem. The rules for this feature should be very simple and unambiguous or else it isn't worth it IMHO.

I'm not sure the examples are a problem. Check my other post for some reasons.
Personally I'm starting to think there are too many potentially confusing 
situations with parsing and semantics but a way out is to model "for" loops 
more closely: separate the declarations from the expressions. Something like
  if (qwert* yuiop=asdfg; yuiop != 0)
in which case it's starting to look pretty similar to how one would write it 
today. In other words the code
  if (decl1, decl2, ... declN ; expr) stmt else stmt
is the same as
  {decl1; decl2; declN; if (expr) stmt else stmt}

I'd like to consider this "plan B" if the other more complete one doesn't work. But it would be a big limitation. My original idea is not specific to if/for/while/etc. constructs. It's a much more general idea: All declarations would serve as expressions, inheriting the type and value. When the scope is one of the special constructs, it would work as you suggested in the other post. That's much more powerful, and it's completely orthogonal. There's no need to alter if/for/while/etc. at all, other than to properly introduce their scope to declarations within. Simple as that. Having said that, I'd gladly take this suggestion over nothing. Some problems I see are 'for' and 'foreach', which already use ';' as a delimiter.
Actually the "if (type var = init; var != 0)" style is arguably more 
readable than the idiom "if ((var = init) != 0)" though one could continue 
to use that idiom "if (type var; (var = init) != 0)"

I disagree. # if (int errorNumber = getError()) print(errorNumber); Versus: # if (int errorNumber; errorNumber = getError()) print(errorNumber); # if (int errorNumber = getError(); errorNumber) print(errorNumber); The first one IMHO is shorter, cleaner and more to the point. Cheers, --AJG.
Aug 11 2005
prev sibling parent reply AJG <AJG_member pathlink.com> writes:
Hi,

There's a possible difficulty here

     if ((qwert * yuiop = asdfg) != 0)

Is this a multiplication or a declaration of a pointer?  At the moment, 
this is syntactically a multiplication.  Though not semantically valid. 

So then, how does this work right now? # qwert * yuiop = asdfg; It can just as well be both. Yet it's valid, right? If qwert is a type, it's a declaration. Otherwise, it's a multiplication. No?
  We could apply the "if it's parseable as a declaration then it's a 
declaration" rule here, but this would also break such semantically 
valid code as

     if (qwert * yuiop == 0)

Same here: # qwert * yuiop == 0 If qwert is a type, it is illegal. Otherwise, it is legal. Or am I missing something? Cheers, --AJG.
Aug 11 2005
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:ddg5l2$osj$1 digitaldaemon.com...
 Hi,

There's a possible difficulty here

     if ((qwert * yuiop = asdfg) != 0)

Is this a multiplication or a declaration of a pointer?  At the moment,
this is syntactically a multiplication.  Though not semantically valid.

So then, how does this work right now? # qwert * yuiop = asdfg; It can just as well be both. Yet it's valid, right? If qwert is a type, it's a declaration. Otherwise, it's a multiplication. No?

The parsing should not depend on the sematics of the symbols - that's what makes parsing C++ templates so hard and as far as I know D has avoided the problem. Some simple experiments will show what is going on: void main() { int qwert,yuiop, asdfg; (qwert * yuiop = asdfg) != 0; // multiply } void main() { int qwert,yuiop, asdfg; qwert * yuiop = asdfg; // declaration } void main() { int qwert,yuiop, asdfg; (qwert * yuiop = asdfg); // multiply } What D does is that a statement that can be parsed either as a expression statement or a declaration statement is chosen to parse as a declaration statement. The compiler has no idea what the semantics of the symbols are when it chooses between the two.
Aug 11 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

The parsing should not depend on the sematics of the symbols - that's what 
makes parsing C++ templates so hard and as far as I know D has avoided the 
problem.

Hm... ok. I don't think this is going to fly in the long-run. How long can it keep avoiding this at the cost of usefulness? Perhaps this is what's holding back implicit template instantiation?
Some simple experiments will show what is going on:
void main() {
    int qwert,yuiop, asdfg;
    (qwert * yuiop = asdfg) != 0; // multiply
}
void main() {
    int qwert,yuiop, asdfg;
    qwert * yuiop = asdfg; // declaration
}

That's an error, though. That's not a declaration; that's a multiply.
void main() {
    int qwert,yuiop, asdfg;
    (qwert * yuiop = asdfg); // multiply
}

What D does is that a statement that can be parsed either as a expression 
statement or a declaration statement is chosen to parse as a declaration 
statement. The compiler has no idea what the semantics of the symbols are 
when it chooses between the two. 

This heuristic fails in the example you gave above. Is that expression illegal then? If so, then this could be illegal too "qwert * yuiop == 0" and the problem is gone. Right? OTOH, if it's not illegal, then the compiler is cheating somewhere, and I'm sure it could cheat again for the "if (qwert * yuiop = asdfg)" case. No? Cheers, --AJG.
Aug 11 2005
parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:ddgdc4$11q6$1 digitaldaemon.com...
 Hi,

The parsing should not depend on the sematics of the symbols - that's what
makes parsing C++ templates so hard and as far as I know D has avoided the
problem.

Hm... ok. I don't think this is going to fly in the long-run. How long can it keep avoiding this at the cost of usefulness? Perhaps this is what's holding back implicit template instantiation?

Implicit instantiation shouldn't cause parsing problems. I think it's a question of semantic complexity (and its impact on compiler complexity and code maintainability). Simple language rules make for maintainable code. Wasn't there just a thread about how D is "easy to parse"? This would make it just as bad as C++. I'm sorry but I think the parsing complexity makes the original proposal not worth it.
Some simple experiments will show what is going on:
void main() {
    int qwert,yuiop, asdfg;
    (qwert * yuiop = asdfg) != 0; // multiply
}
void main() {
    int qwert,yuiop, asdfg;
    qwert * yuiop = asdfg; // declaration
}

That's an error, though. That's not a declaration; that's a multiply.

By error do you mean a compiler error? I think the compiler is behaving as intended. I think you'll have to make a very strong case to get Walter to change the second example to parse like a multiplication. I think Stewart had some threads a while ago about this so you might want to check the archives in either the main newsgroup or the D.bugs newsgroup.
void main() {
    int qwert,yuiop, asdfg;
    (qwert * yuiop = asdfg); // multiply
}

What D does is that a statement that can be parsed either as a expression
statement or a declaration statement is chosen to parse as a declaration
statement. The compiler has no idea what the semantics of the symbols are
when it chooses between the two.

This heuristic fails in the example you gave above. Is that expression illegal then? If so, then this could be illegal too "qwert * yuiop == 0" and the problem is gone. Right?

All the code I posted errors during the semantics phase - after parsing. That's when it figures out, for example, that the result of a multiply can't be an lvalue or that qwert is being used as a type. Try running the code and experimenting to get a feel for the behavior. What D doesn't do is attempt to change the parsing interpretation based on semantic information like "qwert is not a type therefor I'll assume the user meant multiplication" etc.
 OTOH, if it's not illegal, then the compiler is cheating somewhere, and 
 I'm sure
 it could cheat again for the "if (qwert * yuiop = asdfg)" case. No?

I'm not sure what you mean by cheating. I included the example with parens to show that adding parens forces the interpretation to be as an expression instead of a declaration. Your proposal would make it continue to parse as a declaration. The key in your proposal, then, is knowing the semantics of a symbol at parse time - which is a big change from the current compiler strategy.
Aug 11 2005
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
AJG wrote:
 Hi,
 
 
There's a possible difficulty here

    if ((qwert * yuiop = asdfg) != 0)

Is this a multiplication or a declaration of a pointer?  At the moment, 
this is syntactically a multiplication.  Though not semantically valid. 

So then, how does this work right now? # qwert * yuiop = asdfg; It can just as well be both. Yet it's valid, right? If qwert is a type, it's a declaration. Otherwise, it's a multiplication. No?

This is a statement, therefore the compiler tries first to parse it as a declaration. Moreover, assigning a value to a multiplication is meaningless, and so it makes sense that disambiguation rules favour it being a declaration. If there's no initialiser qwert * yuiop; then again it's parsed as a declaration. Treating it as an expression would generally give a nop statement, but this doesn't apply _within_ a statement.
 We could apply the "if it's parseable as a declaration then it's a 
 declaration" rule here, but this would also break such semantically 
 valid code as

    if (qwert * yuiop == 0)

Same here: # qwert * yuiop == 0 If qwert is a type, it is illegal. Otherwise, it is legal. Or am I missing something?

If we allow declarations within expressions, then this becomes ambiguous. Because we could equally be either multiplying qwert by yuiop or declaring yuiop to be a pointer to a qwert, and then comparing the result with zero. Using precedence rules to stop this being a declaration won't help, since (qwert * yuiop) == 0 is equally ambiguous. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Aug 12 2005