January 28, 2013
On Monday, 28 January 2013 at 16:55:40 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 16:50:37 UTC, Maxim Fomin wrote:
>> Than value of this expression (note, that evaluated expression is not b, it is b = c) is assigned to a.
>
> Quoting you (that was exactly part of standard I was referring too):
> "assignment expression has the value of the left operand"
>
> Left operand for (b = c) is b. Thus (b = c) has value of b. Value of b is b.getter(). Thus compiler is re-writing it wrong if we copy C rules.

You are mixing value of expression and expression itself. If assignment expression has value of right operand, it does not mean that in complex assignment like a = b = c , right part of second assignment disappears and it becomes a = b. It is still a = b = c , and a is assigned to value of the whole expression b = c, not just value of b. This means it is a.setter((b = c)) = > a.setter(b.setter(c.getter)). Note, that original program does exactly what is required by ISO C.

>> b.setter cannot be called prior to b.getter as in your example #1 due to sequence rules. The example #2 would be for expression a = c, b = c which is not a = b = c in the presence of properties.
>
> What sequence rules are you speaking about?

About that side effects are sequenced after evaluation, but it is actually irrelevant, because b.getter is not called.
January 28, 2013
On Monday, 28 January 2013 at 16:55:40 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 16:50:37 UTC, Maxim Fomin wrote:
>> Than value of this expression (note, that evaluated expression is not b, it is b = c) is assigned to a.
>
> Quoting you (that was exactly part of standard I was referring too):
> "assignment expression has the value of the left operand"
>
> Left operand for (b = c) is b. Thus (b = c) has value of b. Value of b is b.getter(). Thus compiler is re-writing it wrong if we copy C rules.
>

Applying your logic:

Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).
January 28, 2013
On Monday, 28 January 2013 at 17:20:13 UTC, Maxim Fomin wrote:
> ...

Those are C rules, do not forget it. Thus value of (b = c) IS equivalent to value of b when lvalues are considered. And you are perfectly allowed to forget about c and do a = b after b = c was evaluated. It is plain old data, after all, if b = c is not interchangeable with b, something is wrong.

At least I see nothing in standard that proves your statement.

>
> Applying your logic:
>
> Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).

You are applying it wrong. Thing of it as of recursion.
1) evaluate "a = b = c"
2) evaluate a.set( (b = c).get() ), result of 1 will be a.get() ( if needed )
3) evaluate "b = c"
4) evaluate b.set( c.get() ), result of 3 will be b.get() ( if needed )
5) combine: a.set( b.set( c.get() ), b.get() )

Note that evaluation order for comma expression is defined, so "c.get() ), b.get()" is valid and correct code.

January 28, 2013
On Monday, 28 January 2013 at 03:24:09 UTC, Steven Schveighoffer wrote:
> There are three intentions when creating a function w/ regards to properties:
>
> 1. You intend the function to be called without parentheses to clarify it is a property.
> 2. You intend the function to be only called with parentheses to clarify it is a function.
> 3. You don't care whether it's called with parentheses or not, because the name of the function is clearly a property or a function.

This is a good point. Possibly since I'm the author of the following proposal, but also because I hope it will garner greater consideration than it has so far, this is how you would do these with my suggested language features single-instance structs and opGet:

The new syntax changes:

struct __foo {}
__foo foo;

to:

foo struct {}

This makes single-instance structs as easy to write as lambda functions. Combining them with two new operator definitions opGet and... let me see... opDo should cover it... it would look like this:

struct Goo
{
  int _n;
  foo struct
  {
    int opGet() { return _n; } // Exactly like opCall, but may not be used with parentheses
  }
}

Goo g;
g.foo(); // Error

A struct may have exactly one of opGet, opCall, and opDo.

foo struct
{
  int opGet() { ... }
  int opDo() { ... } // Error: a struct may not have both opGet and opDo
}

opDo does exactly the opposite of opGet: it mandates the uses of parentheses instead of prohibits them. opCall, on the other hand, permits either one freely.

foo struct { int opDo() { return 4; } }
foo; // Error: foo must be called with parentheses

Note how the single-instance struct does not store data of its own, but is used for its value as a namespace and its ability to overload variables. That doesn't mean you *can't* store data in it. It's just like any other struct except that is has what I was thinking of calling a Highlander type: There can be only one!
January 28, 2013
On Monday, January 28, 2013 13:31:34 Jacob Carlborg wrote:
> > Having the compiler lower the following:
> > 
> > @property int a;
> > 
> > to
> > 
> > private int __a;
> > 
> > @property int a() {
> > return __a;
> > }
> > @property int a(int new_a) {
> > 
> > __a=new_a;
> > return __a;
> > 
> > }
> 
> I would love that. But the setter should return void and the compiler should to property rewrites.

Both should work. It's more efficient to chain assignments if the setter returns a value, but chaining should still work if it returns void. It would just be lowered differently in that case.

- Jonathan M Davis
January 28, 2013
On Monday, 28 January 2013 at 17:41:07 UTC, Dicebot wrote:
> On Monday, 28 January 2013 at 17:20:13 UTC, Maxim Fomin wrote:
>> ...
>
> Those are C rules, do not forget it. Thus value of (b = c) IS equivalent to value of b when lvalues are considered. And you are perfectly allowed to forget about c and do a = b after b = c was evaluated. It is plain old data, after all, if b = c is not interchangeable with b, something is wrong.
>
> At least I see nothing in standard that proves your statement.
>

You are still breaking expression a = b = c and mixing terms of value of expression and expression itself.

>>
>> Applying your logic:
>>
>> Left operand for a = (b = c) is a. Thus a has value of (b = c). Value of b = c is b.setter(c.getter()).
>
> You are applying it wrong. Thing of it as of recursion.

I applied exactly as you did.

> 1) evaluate "a = b = c"
> 2) evaluate a.set( (b = c).get() ), result of 1 will be a.get() ( if needed )

.get() is wrong here. Expression is a = b = c, not a = ( (b = c).get ))

> 3) evaluate "b = c"
> 4) evaluate b.set( c.get() ), result of 3 will be b.get() ( if needed )
> 5) combine: a.set( b.set( c.get() ), b.get() )

This breaks requirement from quoted paragraph that side effects are sequenced after value computations on both operands: computing value of b (b.get) happened after updating value (b.set).

In Bugzilla there were close to this case like following (do not remember #):

struct S { int a; int b; int c; }
...
S s1, s2;

s1 = { 1, s1.a 2 }

Compiler was wrongly applied side effects before evaluating s1.a (it should be zero, not 1).

> Note that evaluation order for comma expression is defined, so "c.get() ), b.get()" is valid and correct code.

Well, discussion went repetitive - I still insists that a = b = c should be evaluated as it is currently a.set(b=c) = > a.set(b.set(c)) => a.set(b.set(c.get)). By the way, this C# program also behaves as I understand rules:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class S
{
    int _i;
    public int i
    {
        get { Console.WriteLine("getter"); return _i; }
        set { Console.WriteLine("setter"); _i = value; }
    }
}

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            S s1 = new S() , s2 = new S(), s3 = new S();
            s1.i = s2.i = s3.i;
        }
    }
}

getter
setter
setter
January 28, 2013
On 2013-01-28 15:00, Maxim Fomin wrote:

> Returning void instead of int in the example break assignment chaining a
> = b = c. Besides, how such implicitly defined functions may call user
> defined code (check input validity, call events, etc.)?

No, the compiler should do a rewrite, as follows:

class Foo
{
    int bar_;

    @property int bar () { return bar_; }
    @property void bar (int value) { bar_ = value; }
}

auto foo = new Foo;
int a = foo.bar = 3;

The above line should be rewritten as:

foo.bar = 3;
int a = foo.bar;

The compiler also need to rewrite the following:

struct Bar
{
    int a;
}

class Foo
{
    Bar bar_;

    @property Bar bar () { return bar_; }
    @property void bar (Bar value) { bar_ = value; }
}

auto foo = new Foo;
foo.bar.a = 3;

The above line should be rewritten to:

auto __tmp = foo.bar;
__tmp.a = 3;
foo.bar = __tmp;

If not, the value of "foo.bar.a" hasn't really changed since you returned a copy by value. If you instead return by reference you can bypass the setter using the getter.

-- 
/Jacob Carlborg
January 28, 2013
On 2013-01-28 17:21, Steven Schveighoffer wrote:

> I think Jacob's point is that a = b = c would lower to:
>
> b = c;
> a = b;

This is how the semantics should be. This also shows a clear complete example:

http://forum.dlang.org/thread/uxhgbxdsselokcdkvltx@forum.dlang.org?page=14#post-ke6l44:242mfh:241:40digitalmars.com

> But I think it would be wasteful in the given case.  __a is already in
> the register, I think actually the return __a is a noop.
>
> In other cases, where the property value may be a large struct or
> whatnot, not returning the new value from a setter would make sense.
>
> It would be nice if the compiler made the right choice depending on
> whether you returned a value from the property or not.

Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

-- 
/Jacob Carlborg
January 29, 2013
On Mon, 28 Jan 2013 15:00:41 -0500, Jacob Carlborg <doob@me.com> wrote:

> On 2013-01-28 17:21, Steven Schveighoffer wrote:

>> But I think it would be wasteful in the given case.  __a is already in
>> the register, I think actually the return __a is a noop.
>>
>> In other cases, where the property value may be a large struct or
>> whatnot, not returning the new value from a setter would make sense.
>>
>> It would be nice if the compiler made the right choice depending on
>> whether you returned a value from the property or not.
>
> Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

Like I said, the synthesizing of properties can be done via a library.
The compiler simply needs to call the getter when the setter returns void,
or return the setter's return value when it returns a value.

-Steve
January 29, 2013
On Mon, 28 Jan 2013 15:00:41 -0500, Jacob Carlborg <doob@me.com> wrote:

> On 2013-01-28 17:21, Steven Schveighoffer wrote:

>> But I think it would be wasteful in the given case.  __a is already in
>> the register, I think actually the return __a is a noop.
>>
>> In other cases, where the property value may be a large struct or
>> whatnot, not returning the new value from a setter would make sense.
>>
>> It would be nice if the compiler made the right choice depending on
>> whether you returned a value from the property or not.
>
> Then it's up to the compiler to implement them, I don't need to know the details in the optimizations it can do.

Like I said, the synthesizing of properties can be done via a library.
The compiler simply needs to call the getter when the setter returns void,
or return the setter's return value when it returns a value.

-Steve
4 5 6 7 8 9 10 11 12 13 14 15
Top | Discussion index | About this forum | D home