February 03, 2013
On Sun, 03 Feb 2013 11:11:05 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 2/3/13 5:14 AM, Johannes Pfau wrote:
>> "If a function returns a reference, then assignment through the
>> paren-less call should work: "
>>
>> This is the only part where I would disagree. Why is this special rule
>> necessary if we have full @property support? I think this would still
>> allow too many false positives.
>
> (As a note, this is the current behavior.)
>
> The way I see it is this is a natural consequence of optional parens. The name of a function without a "&" prepended or a "()" after it will invoke the function, and that's that.
>
>> One important aspect that this proposal doesn't cover yet is whether we
>> want to allow "semantic rewriting" for properties:
>> ----
>> Struct a;
>> a.property++; //would this be legal?
>> ----
>
> It's dangerous to get too clever about that. Anyhow, such rewrites are possible:
>
> ++a.p ----> { auto v = a.p; ++v; a.p = v; return v; }()
> a.p++ ----> { auto v = a.p; ++a.p; return v; }()
>
> and so on.
>
>> for other corner cases this list is a good start:
>> http://wiki.dlang.org/Property_Discussion_Wrap-up#Implementation_concerns
>>
>> * Can we get a reference to the property? What does
>>    &x.property mean?
>
> We need to add this to the proposal. There are two schools of thought here:
>
> 1. Make properties emulate regular variables as much as possible. In that case &a.p is the same as &(a.p), i.e. it applies to the returned value. (One counter-argument here is that properties should seldom return a reference because that breaks encapsulation.)
>
> 2. Allow people to do whatever they need to do without much aggravation. In that case &a.p obeys the normal rules of taking a method's address, and &(a.p) applies to the returned value.
>
> I favor (2) and put it in http://wiki.dlang.org/DIP23. Will talk to Walter.

This sounds good.  It might be a bit confusing, but nice in the fact that &(a.b) always means address of whatever b returns, whether it is a field or property.

>> * Are UFCS properties possible? How do they work exactly?
>> * How do you disambiguate property functions when they're free
>>    functions which conflict?
>
> I think it would be best to simply disallow parameterless module-level properties.
>
> // at top level
> @property int foo(); // error
> @property int goo(int); // fine, assume a getter for int

I think this is a possible solution, and I can live with that, I'm fairly certain that some people use global properties currently, so they will not be too happy.

But having to set global properties using a function call (global getters can simply be paren-less functions) isn't horrible.

>> * Are templated properties allowed?
>> ** The access syntax for properties doesn't allow providing types
>
> No. Templated member variables are not allowed either.

Wait, what?  I don't like this idea.  Why should this not be allowed:

@property void x(T)(T t) {_x = to!(typeof(_x))(t);}

As for templates that return a templated type, it should be allowed, although the calling syntax would have to be for the explicit template call, since you can't do IFTI on return values.  In other words:

template x(T) { @property T x(){return to!(T)(_x);} }  // could be shortened to normal @property T x(T)()

auto xcopy = obj.x!(string).x;

Certainly, we need templated getters and setters at module levels:

@property T front(T)(T[] r) { return r[0];}

-Steve
February 03, 2013
On Sun, 03 Feb 2013 11:28:52 -0500, Steven Schveighoffer <schveiguy@yahoo.com> wrote:


> As for templates that return a templated type, it should be allowed, although the calling syntax would have to be for the explicit template call, since you can't do IFTI on return values.  In other words:
>
> template x(T) { @property T x(){return to!(T)(_x);} }  // could be shortened to normal @property T x(T)()
>
> auto xcopy = obj.x!(string).x;

Hm... I actually am changing my mind on this, you should be able to do:

auto xcopy = obj.x!(string);

For the simple reason that if @property is omitted, this would work (simple lack of parens).  It makes no sense to make properties not have this ability.

-Steve
February 03, 2013
Am Sun, 03 Feb 2013 11:11:05 -0500
schrieb Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>:

> On 2/3/13 5:14 AM, Johannes Pfau wrote:
> > "If a function returns a reference, then assignment through the paren-less call should work: "
> >
> > This is the only part where I would disagree. Why is this special rule necessary if we have full @property support? I think this would still allow too many false positives.
> 
> (As a note, this is the current behavior.)
> 
> The way I see it is this is a natural consequence of optional parens. The name of a function without a "&" prepended or a "()" after it will invoke the function, and that's that.

I didn't think of it as a call without parentheses but it indeed makes sense.

> 
> > * Is returning ref values from the getter OK?
> 
> I see no reason to disallow it at the language level.

You have to consider cases though where you have both a setter and a getter returning a 'ref value'.

@property ref int value();
@property void value(int new);

* Is it valid to define both at the same time?
* Which one is used for assignment etc?

> > * Is taking ref values in the setter OK?
> 
> How do you mean that?

@property int value(ref int new);

IIRC that was just meant to complement the "is returning ref OK". Usually nothing bad can happen. Although the ref parameter could be converted into a pointer with some nasty tricks it's probably not the job of the language to prevent that.


February 03, 2013
On 02/03/2013 05:11 PM, Andrei Alexandrescu wrote:
> ...
>
> 2. Allow people to do whatever they need to do without much aggravation.
> In that case &a.p obeys the normal rules of taking a method's address,
> and &(a.p) applies to the returned value.
>
> I favor (2) and put it in http://wiki.dlang.org/DIP23. Will talk to Walter.
>

That is horrible! The entire language construct is necessarily a bit of a patchwork, but there is no reason to aggravate this gratuitously.

> ...
>> * Are templated properties allowed?
>> ** The access syntax for properties doesn't allow providing types
>
> No.

Ditto.

> Templated member variables are not allowed either.
>

(Actually they are silently rewritten to static member variables. I do not like that.)
February 03, 2013
On 2/3/13 7:37 AM, kenji hara wrote:
> 1. When a function is annotated with @property, it cannot be called with
> parenthesis syntax.
> 2. 0-arg functions which not annotated with @property can be called
> without parentheses.
> 3. Ref return getter can make "auxiliary setter", if formal getter is
> missing.
> 4. `typeof(exp)` never returns "function type". In other words, the
> actual type of `exp` and `typeof(exp)` should be same.
> 5. Both `&prop` and `&func` should return function pointer / delegate object
> 6. UFCS CAN NOT call global setter by getter syntax.

Yah. There is still some inconsistency, but I think there's no way to be 100% consistent. For properties &a.prop is not the same as &(a.prop), which is unlike other expressions.

> I think that 4 to 6 are important points of this DIP. Based on the
> rules, I could write an exhaustive test case.

I'll insert comments inline.

> alias Type = int;
>
> unittest
> {
>      struct S
>      {
>          @property Type foo();       // formal getter
>          @property void bar(Type);   // formal setter
>          @property ref Type baz();   // ref return getter == auxiliary
> setter
>      }
>      S s;
>      static assert( __traits(compiles, { s.foo;     }));
>      static assert(!__traits(compiles, { s.foo();   }));
>      static assert(is(typeof(s.foo) == Type));
>      static assert(is(typeof(&s.foo) == Type delegate()));

Yes, great. You may want to also add:

static assert(!__traits(compiles, { auto p = &(s.foo);   }));

because that would apply & to the Type rvalue returned by s.foo.

>      static assert( __traits(compiles, { s.bar = 1; }));
>      static assert(!__traits(compiles, { s.bar(1);  }));
>      static assert(is(typeof(s.bar)) == false);
>      static assert(is(typeof(&s.bar) == void delegate(Type)));

Yes. Also:

static assert(is(typeof(s.bar = 1) == void));

>      static assert( __traits(compiles, { s.baz;     }));
>      static assert(!__traits(compiles, { s.baz();   }));
>      static assert( __traits(compiles, { s.baz = 1; }));
>      static assert(is(typeof(s.baz) == Type));
>      static assert(is(typeof(&s.foo) == ref Type delegate()));

Yes, assuming you meant "baz" on the last line, too.

> }
> unittest
> {
>      struct S
>      {
>          Type foo();         // 0-arg function
>          void bar(Type n);   // 1-arg function
>          ref Type baz();     // 0-arg ref return function
>      }
>      S s;
>      static assert( __traits(compiles, { s.foo;     }));
>      static assert( __traits(compiles, { s.foo();   }));
>      static assert(is(typeof(s.foo) == Type));
>      static assert(is(typeof(&s.foo) == Type delegate()));

Correct. Also add:

static assert(!is(typeof(&(s.foo))));

>      static assert(!__traits(compiles, { s.bar = 1; }));
>      static assert( __traits(compiles, { s.bar(1);  }));
>      static assert(is(typeof(s.bar)) == false);
>      static assert(is(typeof(&s.bar) == void delegate(Type)));

Correct. Also:

static assert(!is(typeof(&(s.bar))));

because the expression s.bar is meaningless. (This is NEW BEHAVIOR.) The basic idea here is to disallow expressions that can't be typed.

>      static assert( __traits(compiles, { s.baz;     }));
>      static assert( __traits(compiles, { s.baz = 1; }));
>      static assert( __traits(compiles, { s.baz();   }));
>      static assert(is(typeof(s.baz) == Type));
>      static assert(is(typeof(&s.baz) == ref Type delegate()));
> }

Correct. Also:

static assert(is(typeof(&(s.baz)) == Type*));

> @property Type foo();

I'm not sure we should allow this at module level. (And there is no way to write a corresponding setter.)

> @property void bar(Type);
> @property ref Type baz();

I think we should disallow this as well.

> unittest
> {
>      static assert( __traits(compiles, { foo;     }));
>      static assert(!__traits(compiles, { foo();   }));
>      static assert(is(typeof(foo) == Type));
>      static assert(is(typeof(&foo) == Type function()));

If we disallow top-level global @properties neither of these will compile. If we do allow them, then the asserts would pass.

>      static assert( __traits(compiles, { bar = 1; }));
>      static assert(!__traits(compiles, { bar(1);  }));
>      static assert(is(typeof(bar)) == false);
>      static assert(is(typeof(&bar) == Type function()));

Nope, all of these are getters for int. The following should compile instead:

static assert( __traits(compiles, { 1.bar; }));
static assert(!__traits(compiles, { bar(1);  }));
static assert(is(typeof(bar)) == false);
static assert(is(typeof(&bar) == void function(int)));

>      static assert( __traits(compiles, { baz;       }));
>      static assert(!__traits(compiles, { baz();     }));
>      static assert( __traits(compiles, { baz = 1;   }));
>      static assert(!__traits(compiles, { baz() = 1; }));
>      static assert(is(typeof(baz) == Type));
>      static assert(is(typeof(&baz) == ref Type function()));

If we allow top-level properties with 0 parameters, these should inded compile.

> }
>
> @property Type foh(Type);

This is always a getter for Type returning a Type.

> @property void bah(Type n, Type m);

This is always a setter having Type on the left-hand side and Type on the right-hand side.

> @property ref Type bas(Type);

This is always a getter for Type.

> Type hoo(Type);
> void var(Type, Type);
> ref Type vaz(Type);
>
> unittest
> {
>      static assert( __traits(compiles, { foh = 1; }) &&
> !__traits(compiles, { hoo = 1; }));

No, replace with:

static assert( __traits(compiles, { 1.foh; }) &&
    !__traits(compiles, { hoo = 1; }));

>      static assert(!__traits(compiles, { foh(1);  }) &&
>   __traits(compiles, { hoo(1);  }));

Correct.

>      static assert(!__traits(compiles, { 1.foh;   }) &&
>   __traits(compiles, { 1.hoo;   }));

Incorrect, foh is a getter for Type so the first should work.

static assert(__traits(compiles, { 1.foh;   }) &&
   __traits(compiles, { 1.hoo;   }));

>      static assert(!__traits(compiles, { 1.foh(); }) &&
>   __traits(compiles, { 1.hoo(); }));

This is identical to the one above.

>      static assert(!__traits(compiles, { bah(1, 2); }) &&
>   __traits(compiles, { var(1, 2); }));

Correct.

>      static assert( __traits(compiles, { 1.bah = 2; }) &&
> !__traits(compiles, { 1.var = 2; }));

Correct.

>      static assert(!__traits(compiles, { 1.bah(2);  }) &&
>   __traits(compiles, { 1.var(2);  }));

First expression fails.

>      static assert( __traits(compiles, { bas = 1;     }) &&
> !__traits(compiles, { vaz = 1;     }));

Both branches fail.

>      static assert(!__traits(compiles, { bas(1);      }) &&
>   __traits(compiles, { vaz(1);      }));

Correct.

>      static assert(!__traits(compiles, { bas(1) = 2;  }) &&
>   __traits(compiles, { vaz(1) = 2;  }));

Correct.

>      static assert(!__traits(compiles, { 1.bas;       }) &&
>   __traits(compiles, { 1.vaz;       }));

The first branch fails because 1.bas is legit.

>      static assert(!__traits(compiles, { 1.bas = 2;   }) &&
>   __traits(compiles, { 1.vaz = 2;   }));

First branch fails because 1.bas is legit and yields a ref int. Second branch passes.

>      static assert(!__traits(compiles, { 1.bas();     }) &&
>   __traits(compiles, { 1.vaz();     }));

Correct.

>      static assert(!__traits(compiles, { 1.bas() = 2; }) &&
>   __traits(compiles, { 1.vaz() = 2; }));
> }

Correct.

> Is this correct?

As noted above, I might have missed some. I will update the doc with a copy of your unittests. Thanks!


Andrei

February 03, 2013
On 2/3/13 7:40 AM, kenji hara wrote:
> 2013/2/3 Steven Schveighoffer <schveiguy@yahoo.com
> <mailto:schveiguy@yahoo.com>>
>
>     I have a possible suggestion to fix this within your proposal:
>
>     @property on single-arg free functions ONLY enables a setter mode,
>     it does not work as a UFCS getter.
>
>   [snip]
>
> I was thinking the exact same thing.

Then we can't make this work:

@property ref T front(T[] array) { return array[0]; }

unittest
{
   auto a = [ 1, 2, 3];
   auto b = a.front;
}


Andrei

February 03, 2013
On 2/3/13 7:49 AM, Steven Schveighoffer wrote:
> On Sun, 03 Feb 2013 07:16:17 -0500, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>
>> On Sun, 03 Feb 2013 03:16:08 -0500, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>
>>> Walter and I have had a discussion on how to finalize properties.
>>>
>>> http://wiki.dlang.org/DIP23
>>
>> I agree with everything in this. This looks exactly like what I wanted
>> a few days ago. Thank you!
>>
>> One aspect not addressed is free-function properties:
>
> I thought of one other problem with this proposal. You can no longer get
> a delegate of a property. Like it or not, there will be existing code
> that does get a delegate to properties (it is allowed now).
>
> I think we should provide a __traits accessor for this. Otherwise, there
> is no way to port said code to the new style.

In the proposal &a.b gets the delegate of a property.

Andrei
February 03, 2013
On Sunday, 3 February 2013 at 17:37:03 UTC, Andrei Alexandrescu wrote:
> In the proposal &a.b gets the delegate of a property.
>

:facepalm:
February 03, 2013
On 2/3/13 7:59 AM, Timon Gehr wrote:
> On 02/03/2013 09:16 AM, Andrei Alexandrescu wrote:
>> Walter and I have had a discussion on how to finalize properties.
>>
>> http://wiki.dlang.org/DIP23
>> ...
>
> Looks good. Two things:
>
> What about eg:
>
> @property T front(T)(T[] arr){ return arr[0]; }

I updated the doc to clarify that one-parameter properties are ALWAYS getters and two-parameter properties are ALWAYS setters.

> And:
>
> "Avoid embarrassing situations such as expressions with unexpressible
> types or no-op address-of operator (as is the case with C functions)."
>
> The DIP does not fix the first issue.
>
> pragma(msg, typeof(*(int x)=>x)); // pure nothrow @safe int(int x)

I see. I think dereferencing a function or delegate type should never work.

Andrei
February 03, 2013
On 2/3/13 8:16 AM, FG wrote:
> On 2013-02-03 09:16, Andrei Alexandrescu wrote:
>> Walter and I have had a discussion on how to finalize properties.
>
> Very reasonable in the majority of the proposal.
> I can't, however, get my head around 2-arg property functions:
>
> int a_;
> @property void a(int v, bool rev=false) { a_ = rev ? -v : v; }

I'd say never allow defaulted arguments with @property.

> a = 10; // fine
> 10.a = true; // this feels backwards, true is just an extra flag
>
> The boolean flag is optional and making it the rhs of the assignment
> suggests it is the value which will be set. On the other hand switching
> argument positions, resulting in true.a = 10 also feels very strange.
>
> I'd say: allow only 0 or 1 argument in properties.
> If you need more arguments, write a normal function for it.

Two-argument @properties are always setters.


Andrei