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


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


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






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


August 12, 2005
Hi,

>> I will assume by if/for/while you also meant switch/foreach/etc. right?
>Yup, any statement that has a sub-statement..

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.





August 12, 2005
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.
1 2 3
Next ›   Last »