August 10, 2005
"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.


August 10, 2005
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);
>Now you're gettin' my point...

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.


August 11, 2005
>>> 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);
>>Now you're gettin' my point...
>
> 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.



August 11, 2005
>>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".


August 11, 2005
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.





August 11, 2005
"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.


August 11, 2005
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.
August 11, 2005
> 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)"


August 11, 2005
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.




August 11, 2005
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.