February 06, 2013
On 02/06/2013 04:32 PM, Andrei Alexandrescu wrote:
> On 2/5/13 7:39 PM, Timon Gehr wrote:
>> As my posts in the DIP23 thread have been left unanswered, I have
>> prepared a counter proposal for DIP23 during the last hour.
>>
>> Everything DIP23 addresses is specified in the two short sub-sections
>> "Optional parens" and "@property: basic design".
>>
>> Those in favour of what was called "semantic rewrites" in the DIP23
>> thread should probably read on further.
>>
>> All parts of this proposal are independent of DIP24 (which Andrei is
>> preparing).
>>
>> http://wiki.dlang.org/DIP23_Counter_Proposal
>>
>> There are almost no examples yet, but in case it is not clear how some
>> case would be handled, feel free to ask.
>
> So syntactic case (2) prescribes that when foo is in address-taken
> position "&foo", that means take its address as opposed to evaluate foo.
> That makes the use of "&" here sheer punctuation, as opposed to operator.
>

Here and everywhere else.

auto x = 5;
auto y = &5; // error!
auto z = &x; // ok!

> There's also "(Note that redundant parentheses are assumed to be dropped
> in the parsing stage.)" It's unclear whether this makes &(foo) and
> &(((foo))) identical to &foo. ...

That is exactly the case it describes. Yes it makes them identical.

> If they do, in this context parens are also
> punctuation, not expression grouping; they don't enclose an expression,
> but instead define &(((...(foo)...))) as a sole syntactic unit (!).
>

They are neither. They are just insignificant information thrown away by the parser.

> This also leads to potential confusion, seeing as &(foo) takes the
> address of foo, but &( true ? foo : bar ) does, in fact, take the
> address of whatever foo returns.

Ah, very good point! But please note that this is 100% a ternary operator issue.

Interestingly, what you describe is how it actually works in Scala. I do not feel strongly about this, but we might as well do it the other way.

I have clarified what it means to occur in a context. I think the ternary expression is the only composite expression that can occur in lvalue position that behaves this way.

This change also 'fixes' the related case (true ? foo : bar)().

> This makes the role of "&", "(", and
> ")" in the proposal as both punctuation and operator painfully visible.
>

The syntax tree looks like this:

      &
      |
      ?
    / | \
   /  |  \
true foo bar

There are no "(" ")". Why would they affect semantics?

> This all makes DIP24 fail meet its own definition of success as far as I
> understand it, i.e. keeping "&" to mean operator and parens to mean
> grouping.

1. There is no point where the DIP calls "&" an "operator".
2. It succeeds in keeping parens mean grouping. (Given any of the two behaviours of ternary expressions.)

DIP24 meets its own definition of success.

> In my opinion, it also makes DIP24 fail to improve over DIP23.
> ...

In my opinion too. It conveniently also makes all other statements true, as the line of reasoning crucially depends on fallaciously ascribing properties of the ternary operator to the parens enclosing it.

(The second fallacy is ascribing the properties of rvalue vs. lvalue distinction inherited from C to DIP24.)

> DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
> and &expr.foo are indivisible syntactic units; thus parentheses are not
> ascribed a special meaning by DIP23.

That is inaccurate, and it is special enough that I do not care to fully formalize the special meaning ascribed to parentheses by DIP23.

Basically it makes use of parens affect whether some position in the syntax tree is treated as lvalue or rvalue position in some special cases.

> On the contrary, as soon as parens
> are used, as in &(foo) or &( true ? foo : bar ), the usual meaning
> of parens enters in action and give the expression inside the usual meaning.
>

You mean the usual rvalue position meaning, eliminating the lvalue position meaning, even if the expression actually occurs in an lvalue position. That is unusual.

> I should point out that that's all okay, but we got to be clear about
> it. Tokens are used as both punctuation and operators all the time. For
> example, it may sound weird that (stuff) and ((stuff)) should ever mean
> distinct things,

They never do and never should.

> but writeln((1, 2)) does something different from
> writeln(1, 2).

I guess this attempts to instantiate 'stuff' with "1, 2"?

The substring "((1, 2))" in the first example does not correspond to a node in the syntax tree. What is the point?

> This is because the outer layer is punctuation and the
> inner layer is expression grouping.

One of the usages is unary and the other is binary. Another example of this is ~x and x~y. This also shows that the two usages do not have to be related in any way, so nothing is weird.

> And nobody blinks an eye.

Of course they do. This situation is one cause of the extensive built-in tuple discussions.

> The language just needs to be as clear as possible about which is which.
>
> If DIP25 gets approved, taking the address of ref results will be
> banned, and therefore that potential meaning of "&" disappears, which
> simplifies things.

How? It only adds code to my front-end to make sure the case is actually caught and properly reported. It does not even simplify the byte code compiler. (Because it uses similar code paths for the &foo() and foo()=exp cases.)

> But we'll still have to live with the reality that in
> the constructs "&foo" and "&expr.foo", "&" is not an operator.

As well as in all other constructs where it is used in unary form.

> And that's okay.
>

Except for the unfortunate confusion it can cause.


February 06, 2013
On 2/6/13 12:41 PM, Timon Gehr wrote:
>> If they do, in this context parens are also
>> punctuation, not expression grouping; they don't enclose an expression,
>> but instead define &(((...(foo)...))) as a sole syntactic unit (!).
>>
>
> They are neither. They are just insignificant information thrown away by
> the parser.

Nononono. Hold them horses. We can't gloss over things like that. That means in this context parens don't have the usual meaning of expression grouping, because what's inside is not an expression. They are punctuation that makes &((...(symbol)...)) one syntactic unit identical to &symbol.

>> This also leads to potential confusion, seeing as &(foo) takes the
>> address of foo, but &( true ? foo : bar ) does, in fact, take the
>> address of whatever foo returns.
>
> Ah, very good point! But please note that this is 100% a ternary
> operator issue.

Not at all. What do these mean?

&({ return fun; }())
&(fun, fun)

The last one is really fun because the same thing may be evaluated first and then have its address taken. Wow. But of course it doesn't. Why? Because "&" and parens have special meaning for &(fun), which is my point.

>> This all makes DIP24 fail meet its own definition of success as far as I
>> understand it, i.e. keeping "&" to mean operator and parens to mean
>> grouping.
>
> 1. There is no point where the DIP calls "&" an "operator".

It doesn't have to. It's everywhere else in the language. That's my point: new meaning of "&".

> 2. It succeeds in keeping parens mean grouping. (Given any of the two
> behaviours of ternary expressions.)
>
> DIP24 meets its own definition of success.

Of course, if moving the goalposts is an option. My understanding was that the express purpose of DIP24 was to avoid potential confusion and avoid ascribing new meaning to "&" or parens. As far as I can tell it failed at both: "&" is sometimes not an operator, and parens sometimes don't encompass expressions.

>> In my opinion, it also makes DIP24 fail to improve over DIP23.
>> ...
>
> In my opinion too. It conveniently also makes all other statements true,
> as the line of reasoning crucially depends on fallaciously ascribing
> properties of the ternary operator to the parens enclosing it.

That got destroyed above.

> (The second fallacy is ascribing the properties of rvalue vs. lvalue
> distinction inherited from C to DIP24.)
>
>> DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
>> and &expr.foo are indivisible syntactic units; thus parentheses are not
>> ascribed a special meaning by DIP23.
>
> That is inaccurate, and it is special enough that I do not care to fully
> formalize the special meaning ascribed to parentheses by DIP23.

DIP23 does not ascribe any special meaning to parentheses. That's actually part of the point and a good thing about it. I think there's a misunderstanding somewhere.

> Basically it makes use of parens affect whether some position in the
> syntax tree is treated as lvalue or rvalue position in some special cases.

No, that is incorrect.

>> If DIP25 gets approved, taking the address of ref results will be
>> banned, and therefore that potential meaning of "&" disappears, which
>> simplifies things.
>
> How? It only adds code to my front-end to make sure the case is actually
> caught and properly reported. It does not even simplify the byte code
> compiler. (Because it uses similar code paths for the &foo() and
> foo()=exp cases.)

I think DIP24 means more code to the front end, more special rules, more explaining, and more confusion than DIP23.

>> But we'll still have to live with the reality that in
>> the constructs "&foo" and "&expr.foo", "&" is not an operator.
>
> As well as in all other constructs where it is used in unary form.
>
>> And that's okay.
>>
>
> Except for the unfortunate confusion it can cause.

And that DIP24 does not get rid of.


Andrei
February 06, 2013
On 02/06/2013 08:09 PM, Andrei Alexandrescu wrote:
> On 2/6/13 12:41 PM, Timon Gehr wrote:
>>> If they do, in this context parens are also
>>> punctuation, not expression grouping; they don't enclose an expression,
>>> but instead define &(((...(foo)...))) as a sole syntactic unit (!).
>>>
>>
>> They are neither. They are just insignificant information thrown away by
>> the parser.
>
> Nononono. Hold them horses. We can't gloss over things like that.

We don't! What I have stated is accurate. (You can check the DMD source, if that helps.)

> That means in this context parens don't have the usual meaning of expression
> grouping, because what's inside is not an expression.

PrimaryExpression:
    Identifier
    ...

> They are punctuation that makes &((...(symbol)...)) one syntactic unit identical
> to &symbol.
>

Again, &((...(symbol)...)) and &symbol are both serialized representations of

   &
   |
 symbol

I.e. they mean the same thing.

>>> This also leads to potential confusion, seeing as &(foo) takes the
>>> address of foo, but &( true ? foo : bar ) does, in fact, take the
>>> address of whatever foo returns.
>>
>> Ah, very good point! But please note that this is 100% a ternary
>> operator issue.
>
> Not at all.

I meant 'as opposed to a paren issue'.

&( true ? foo : bar ) // <-- note the ternary operator

> What do these mean?
>
> &({ return fun; }())

That is equivalent to

&(&__dgliteral0)()

where

auto __dgliteral0(){ return fun; }


> &(fun, fun)
>

Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.

> The last one is really fun because the same thing may be evaluated first
> and then have its address taken. Wow. But of course it doesn't. Why?
> Because "&" and parens have special meaning for &(fun), which is my point.
>

Now you are saying that parens _do_ have a special meaning?

>>> This all makes DIP24 fail meet its own definition of success as far as I
>>> understand it, i.e. keeping "&" to mean operator and parens to mean
>>> grouping.
>>
>> 1. There is no point where the DIP calls "&" an "operator".
>
> It doesn't have to. It's everywhere else in the language.

No way. I repeat:

auto x = 5;
auto y = &5; // error!
auto z = &x; // ok!

> That's my point: new meaning of "&".
>...

Obviously your reading of my post was quite shallow.

>> 2. It succeeds in keeping parens mean grouping. (Given any of the two
>> behaviours of ternary expressions.)
>>
>> DIP24 meets its own definition of success.
>
> Of course, if moving the goalposts is an option.

It is. DIP24 is in draft stage. Moving the goalposts was not necessary so far though.

> My understanding was that the express purpose of DIP24 was to avoid potential confusion and avoid ascribing new meaning to "&" or parens.
> As far as I can tell it failed at both: "&" is sometimes not an operator, and parens sometimes don't encompass expressions.

"&" is never an operator and parens always encompass expressions. Additionally, there is a bunch of rewrite rules to get parentheses on function calls optional.

>>> In my opinion, it also makes DIP24 fail to improve over DIP23.
>>> ...
>>
>> In my opinion too. It conveniently also makes all other statements true,
>> as the line of reasoning crucially depends on fallaciously ascribing
>> properties of the ternary operator to the parens enclosing it.
>
> That got destroyed above.
>

No. It's just that both ternary expressions and comma expressions can propagate lvalue-positions, and I had forgotten about the comma expression as I was in a hurry.

>> (The second fallacy is ascribing the properties of rvalue vs. lvalue
>> distinction inherited from C to DIP24.)
>>
>>> DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
>>> and &expr.foo are indivisible syntactic units; thus parentheses are not
>>> ascribed a special meaning by DIP23.
>>
>> That is inaccurate, and it is special enough that I do not care to fully
>> formalize the special meaning ascribed to parentheses by DIP23.
>
> DIP23 does not ascribe any special meaning to parentheses.

It does. To the point that the parser has to be changed, just in order to implement rewrite rules.

> That's actually part of the point and a good thing about it.
> I think there's a misunderstanding somewhere.
>

Definitely.

>> Basically it makes use of parens affect whether some position in the
>> syntax tree is treated as lvalue or rvalue position in some special cases.
>
> No, that is incorrect.
>

It is correct.

&fun   // <-- fun occurs in lvalue position
&(fun) // <-- (fun) occurs in lvalue position

You are now arguing for making the 'fun''s in "fun" and "(fun)" behave differently. The first should behave as if it was in address-taken (lvalue) position and the second should behave as if it was not (as if it was in rvalue position).

The rules for context dependency were intended to closely parallel those of lvalue position propagation in order to minimize the amount of new rules. If you see any other differences, please step up.

>>> If DIP25 gets approved, taking the address of ref results will be
>>> banned, and therefore that potential meaning of "&" disappears, which
>>> simplifies things.
>>
>> How? It only adds code to my front-end to make sure the case is actually
>> caught and properly reported. It does not even simplify the byte code
>> compiler. (Because it uses similar code paths for the &foo() and
>> foo()=exp cases.)
>
> I think DIP24 means more code to the front end, more special rules,

Well, you are wrong, and as far as I can tell, the speculation is entirely unfounded.

DIP23 is not even _implementable_ currently, because it does not even attempt to handle all cases that are necessary to handle. In order to do that, it would need to copy most of the rules of DIP24. Then, making &fun and &(fun) behave differently is more work than making them behave the same.

> more explaining,  and more confusion than DIP23.
>

I do not have any hard data on this, but &fun and &(fun) meaning different things is extremely confusing. Note that this is not actually part of DIP23 yet for non-@property functions. (It is just left unspecified.) I guess this is an oversight?

>>> But we'll still have to live with the reality that in
>>> the constructs "&foo" and "&expr.foo", "&" is not an operator.
>>
>> As well as in all other constructs where it is used in unary form.

Still holds.

>>> And that's okay.
>>>
>>
>> Except for the unfortunate confusion it can cause.
>
> And that DIP24 does not get rid of.

Neither does DIP23. That is not their purpose.


February 07, 2013
On Wednesday, 6 February 2013 at 15:32:33 UTC, Andrei Alexandrescu wrote:
> So syntactic case (2) prescribes that when foo is in address-taken position "&foo", that means take its address as opposed to evaluate foo. That makes the use of "&" here sheer punctuation, as opposed to operator.
>

That is one of the node of the optional () problem. Your DIP have similar issue with &.

> This also leads to potential confusion, seeing as &(foo) takes the address of foo, but &( true ? foo : bar ) does, in fact, take the address of whatever foo returns. This makes the role of "&", "(", and ")" in the proposal as both punctuation and operator painfully visible.
>

As ell, your proposal have similar issues.

> This all makes DIP24 fail meet its own definition of success as far as I understand it, i.e. keeping "&" to mean operator and parens to mean grouping. In my opinion, it also makes DIP24 fail to improve over DIP23.
>
> DIP23 has in fact /fewer/ such problems, because it clarifies that &foo and &expr.foo are indivisible syntactic units; thus parentheses are not ascribed a special meaning by DIP23. On the contrary, as soon as parens are used, as in &(foo) or &( true ? foo : bar ), the usual meaning of parens enters in action and give the expression inside the usual meaning.
>

I do see special meaning as an issue, as it complexity the language. DIP24 is an improvement in that regard over DIP23.

> If DIP25 gets approved, taking the address of ref results will be banned, and therefore that potential meaning of "&" disappears, which simplifies things. But we'll still have to live with the reality that in the constructs "&foo" and "&expr.foo", "&" is not an operator. And that's okay.
>

That is cascading the problem, not solving it.
February 07, 2013
On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
>> &(fun, fun)
>>
>
> Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
>

Adding more special cases are not gonna create a good DIP.

Can't we make foo evaluate the the first class function object ? And then figure out if/when it is safe to auto evaluate first class functions ?

It seems to me like the only direction that do not ends up in an explosion of special cases.
February 07, 2013
On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
> ...
>
> The semantic rewrite stuff is something I don't feel strongly either
> way. It would be nice, but at the same time, it doesn't feel necessary
> to me. One can always simply avoid it by not using the operators on
> properties.
>

A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code.  Forbidding more than half of D's operators on them will definitely break this attribute.
February 07, 2013
On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix <deadalnix@gmail.com> wrote:

> On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
>>> &(fun, fun)
>>>
>>
>> Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
>>
>
> Adding more special cases are not gonna create a good DIP.

I don't they are so much a special case, as they are a clarification.

The two "exceptions" are simply explaining that because ternary operator and comma operators evaluate to an lvalue, it is equivalent to putting the & on the resulting lvalue.

-Steve
February 07, 2013
On Wed, 06 Feb 2013 16:30:09 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:

> I do not have any hard data on this, but &fun and &(fun) meaning different things is extremely confusing.

I can vouch that I find it confusing.  At least on one other occasion, Walter nixed a feature because of this.

It was for const(X) to mean tail-const and const X to mean full const.  The point was made that const(X) and const X should be equivalent, and to do otherwise will result in tremendous confusion.

I see a similar pattern here.

-Steve
February 07, 2013
On 02/07/2013 02:56 PM, Steven Schveighoffer wrote:
> On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix <deadalnix@gmail.com> wrote:
>
>> On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
>>>> &(fun, fun)
>>>>
>>>
>>> Agh, comma strikes again. It should be handled analogous to the
>>> ternary expression. i.e. the expression above evaluates fun and then
>>> returns the function pointer. The DIP now states this. (the second
>>> fun is in address-taken position.) This is in agreement to how lvalue
>>> positions propagate into comma expressions.
>>>
>>
>> Adding more special cases are not gonna create a good DIP.
>
> I don't they are so much a special case, as they are a clarification.
>
> The two "exceptions" are simply explaining that because ternary operator
> and comma operators evaluate to an lvalue, it is equivalent to putting
> the & on the resulting lvalue.
>
> -Steve

Exactly. Those operators already have this behaviour in other contexts.

OT: One of my favourite comma/ternary-isms is the following:

import std.typecons : tuple;
void foo(int x){}
void main(){
    int x;
    foo((x++,tuple.expand),x,(x++,tuple.expand));
    foo(true?tuple.expand:tuple.expand,x,);
}

I think it is valid code. :-)
DMD cannot handle it though.
February 07, 2013
On Wed, 06 Feb 2013 23:26:03 -0500, Chad Joan <chadjoan@gmail.com> wrote:

> On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
>> ...
>>
>> The semantic rewrite stuff is something I don't feel strongly either
>> way. It would be nice, but at the same time, it doesn't feel necessary
>> to me. One can always simply avoid it by not using the operators on
>> properties.
>>
>
> A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code.  Forbidding more than half of D's operators on them will definitely break this attribute.

I find this explanation lacking.  A field supports operations that are impossible to implement with properties, there will never be a non-code-breaking change from a field to a property.  So it's a matter of "how much" breakage you want to allow.  This is really a judgment call, there is no "right" answer.  The closest you can get to a field is to ref an actual field.  But in that case, I don't see the point of the property, since you give unfettered access to the field!

The whole point of having properties that I see is to implement things you CAN'T implement with a field, or are more cumbersome.  Like read-only fields.  Such a change would necessarily and on purpose break code.

-Steve