www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - alias this doesn't work for properties.

reply "JS" <js.mdnq gmail.com> writes:
http://dpaste.dzfl.pl/0c923861

class A
{
     double x;
      property double X() { return x; }
      property double X(double v) { return x = v; }
     alias X this;
}

class B
{
     A a;
      property A Y() { return a; }
      property A Y(A v) { return a = v; }
}


void main()
{
	B b = new B();
	b.Y = 3;
}

error not a property of b.Y



adding refs to the returns of the getters allows the code to work 
but only getters are called(hence the need for refs).

Please allow alias this to work with overloaded functions. 
(specifically properties, b.Y = 3; is not b.Y() = 3 but b.Y(3);)
Aug 01 2013
next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 1 August 2013 at 20:42:08 UTC, JS wrote:
 http://dpaste.dzfl.pl/0c923861

 class A
 {
     double x;
      property double X() { return x; }
      property double X(double v) { return x = v; }
     alias X this;
 }

 class B
 {
     A a;
      property A Y() { return a; }
      property A Y(A v) { return a = v; }
 }


 void main()
 {
 	B b = new B();
 	b.Y = 3;
 }

 error not a property of b.Y



 adding refs to the returns of the getters allows the code to 
 work but only getters are called(hence the need for refs).

 Please allow alias this to work with overloaded functions. 
 (specifically properties, b.Y = 3; is not b.Y() = 3 but b.Y(3);)

b.Y = 3 is rewritten as b.Y(3) You are asking for an "int" to be be implicitly convertable to an "A", which isn't possible.
Aug 01 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Thursday, 1 August 2013 at 21:56:46 UTC, John Colvin wrote:
 On Thursday, 1 August 2013 at 20:42:08 UTC, JS wrote:
 http://dpaste.dzfl.pl/0c923861

 class A
 {
    double x;
     property double X() { return x; }
     property double X(double v) { return x = v; }
    alias X this;
 }

 class B
 {
    A a;
     property A Y() { return a; }
     property A Y(A v) { return a = v; }
 }


 void main()
 {
 	B b = new B();
 	b.Y = 3;
 }

 error not a property of b.Y



 adding refs to the returns of the getters allows the code to 
 work but only getters are called(hence the need for refs).

 Please allow alias this to work with overloaded functions. 
 (specifically properties, b.Y = 3; is not b.Y() = 3 but 
 b.Y(3);)

b.Y = 3 is rewritten as b.Y(3) You are asking for an "int" to be be implicitly convertible to an "A", which isn't possible.

This is wrong because (cast(A)b.Y) = 3; works. b.Y(3) should work because A is aliased to double. One of the issues is b.Y(3) The setter of B is never called. You are right that b.Y(3) does not accept an int(or double) BUT this is a bug in the compiler because it should since 3 can be assigned to it. It's just that the compiler isn't smart enough to see this because of the way it calculates the assignment. b.Y = 3 should do the same as b.a = 3 which should be the same as b.a.x = 3. If you think about what the code is actually doing you will see it should be possible. http://dpaste.dzfl.pl/ac376ca0
Aug 01 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 1 August 2013 at 22:23:40 UTC, JS wrote:
 This is wrong because

 (cast(A)b.Y) = 3;

 works.

That's an interesting case. cast(A)b.Y) is using the B getter property, which makes sense as, due to the cast, nothing in B can be called for the assigment. So you have (in psuedo-code, all properties expanded as getY/setY): b.getY.castToA = 3; wheras the previous example is: b.setY(3); Neither are an int being implicitly converted to an A
 b.Y = 3 should do the same as b.a = 3 which should be the same 
 as b.a.x = 3.

Why that can't work: struct A { double x; alias x this; } struct B { private A a; property A Y() { importantGetPrep(); return a; } property void Y(A v) { importantSetPrep(); a = v; } } void main() { B b; b.Y = 3; //cannot be a call to the setter as cannot convert 3 to A . //cannot be a call to the getter followed by an assigment //as the assignment would then have no effect on b.a //due to value semantics. //cannot be anything else as it would bypass the //important*etPrep that must be called on all accesses //to B.a . } Putting functions like important*etPrep in properties are one of the main reasons why properties are useful, it would be a very bad idea to let them be circumvented by an implementation change in an internal struct.
Aug 01 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Thursday, 1 August 2013 at 23:51:38 UTC, John Colvin wrote:
 On Thursday, 1 August 2013 at 22:23:40 UTC, JS wrote:
 This is wrong because

 (cast(A)b.Y) = 3;

 works.

That's an interesting case. cast(A)b.Y) is using the B getter property, which makes sense as, due to the cast, nothing in B can be called for the assigment. So you have (in psuedo-code, all properties expanded as getY/setY): b.getY.castToA = 3; wheras the previous example is: b.setY(3); Neither are an int being implicitly converted to an A
 b.Y = 3 should do the same as b.a = 3 which should be the same 
 as b.a.x = 3.

Why that can't work: struct A { double x; alias x this; } struct B { private A a; property A Y() { importantGetPrep(); return a; } property void Y(A v) { importantSetPrep(); a = v; } } void main() { B b; b.Y = 3; //cannot be a call to the setter as cannot convert 3 to A . //cannot be a call to the getter followed by an assigment //as the assignment would then have no effect on b.a //due to value semantics. //cannot be anything else as it would bypass the //important*etPrep that must be called on all accesses //to B.a . } Putting functions like important*etPrep in properties are one of the main reasons why properties are useful, it would be a very bad idea to let them be circumvented by an implementation change in an internal struct.

Ok, I don't know why you keep saying 3 cannot be "converted" to A. Do you Realize that the ultimate goal is to assign 3 to x? 3 is an int and x is a double, there is no conversion issues here. if I do a = 3; b.Y = a; then it is no problem. The issue is that b.Y wants an A and ignores the alias, I think this is wrong behavior because when you alias A it is suppose to act like that type... in this case it doesn't. Simple as that. I can see why in some cases it is suppose to act the way it does, e.g., what if B.Y uses other fields of A, you obviously can't use a int then. If this is the case a run time error or compile time error should occur. In any case, I'll just overload the setter to accept double and be done with it. Arguing whether it is a flaw/limitation in D or a flaw/limitation in my thinking won't get the behavior I want. Note, I can just use a setter on double then it will work fine. This is not the behavior I want though because I breaks the homogeneity of my code generation. IMO, if the argument to a setter has an alias this, that type should also work for the argument. If the body of the setter uses things not in the aliased type then it it should be an error(in that case, you should overload the setter). This way you get the logical behavior expected but can also handle the more complex case. code: struct A { value double; int x; alias this value; } class B { property A prop(A v) { A a = new A; a.value = a.x; // error if v is double and no setter for double, remove line to make it work. return a; } // property A prop(double v) { ... } // specialize to prevent error for doubles This way for simple properties one gets the expected behavior for alias this.
Aug 01 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
I'm going to provide a reduced case for the issue you are having.

struct Foo {
     double x;
     alias x this;
}

void main() {
     Foo a;
     a = 8; // Alias this assignment
     assert(a.x == 8);
     fun(7); // What you want
}

void fun(Foo a) {
}

You're claiming that since Foo is requested to "be" a double 
calling fun(7) should just assign 7 to x.

What you're missing is that it is Foo which behaves like double, 
however fun(7) there is no Foo involved, it doesn't exist. You're 
actually requesting that a Foo is created, then to have an 
assignment of 7 to x of the new temporary Foo.

This is not a bug, it is by design. Alias this is sugar of type 
it is declared in not the type it is declared to.
Aug 01 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Friday, 2 August 2013 at 01:46:28 UTC, Jesse Phillips wrote:
 I'm going to provide a reduced case for the issue you are 
 having.

 struct Foo {
     double x;
     alias x this;
 }

 void main() {
     Foo a;
     a = 8; // Alias this assignment
     assert(a.x == 8);
     fun(7); // What you want
 }

 void fun(Foo a) {
 }

 You're claiming that since Foo is requested to "be" a double 
 calling fun(7) should just assign 7 to x.

 What you're missing is that it is Foo which behaves like 
 double, however fun(7) there is no Foo involved, it doesn't 
 exist. You're actually requesting that a Foo is created, then 
 to have an assignment of 7 to x of the new temporary Foo.

 This is not a bug, it is by design. Alias this is sugar of type 
 it is declared in not the type it is declared to.

Um, but I'm not talking about a free function or member function but a property... they are suppose to be special else whats the point. They are suppose to wrap fields. Because of this "design" you speak of, I can't use a double wrapped(essentially) type because it is in an interface and I can't use a field in the interface. What is the difference between class A { int x; } class A { private int _x; property int x() { } property int x(int v) { } } ? From the outside world there is suppose to be no difference. The point of properties is to wrap a type... yet I can't do that.... and also because A is an interface there is not even a fallback to the using a field to get it to work. Anyways, this isn't headed anywhere... It's easy to sit back and try to analyze the situation without having ran into the problem directly.
Aug 01 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 1 August 2013 at 21:56:46 UTC, John Colvin wrote:
 You are asking for an "int" to be be implicitly convertable to 
 an "A", which isn't possible.

http://dpaste.dzfl.pl/c3cce6e2 import std.stdio; class A { double x; property double X() { writeln("From A.Get"); return x; } property double X(double v) { writeln("From A.Set"); x = v; return x; } alias X this; this(double val) { this.x = val; } } class B { A a; A Y() { writeln("From B.Get"); return a; } A Y(A v ...) { writeln("From B.Set"); a = v; return a; } this() { a = new A(0); } } void main() { B b = new B(); writeln(b.Y); b.Y = 3; writeln(b.Y); readln(); }
Aug 01 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Friday, 2 August 2013 at 01:55:35 UTC, JS wrote:
 Anyways, this isn't headed anywhere... It's easy to sit back 
 and try to analyze the situation without having ran into the 
 problem directly.

Is it not headed anywhere? Are you not learning the design choices of D and why those choices where chosen over others? Are you not learning the limitations that will be placed on you so that in the future you can select a design which matches those limitations? D has many flaws and it is missing polish in many places. You've been putting CTFE and other features through their pases, yet instead of feeling that your are learning something you feel that it "isn't headed anywhere?" There is much bickering about what properties are, but one thing we know is they can not act exactly like fields. So you're expectations are off here, and I too am disappointed.
Aug 01 2013
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 2 August 2013 at 00:36:19 UTC, JS wrote:
 Realize that the ultimate goal is to assign 3 to x?

A working solution is to just use opAssign. I think of alias this as being implicit cast to , whereas opAssign is implicit cast from (well, sort of, it only works on assignments, but there's also constructors you can do. Where it falls short of full implicit cast is stuff like function calls) Anyway, try this: class A { double x; property double X() { return x; } property double X(double v) { return x = v; } alias X this; // new A opAssign(double rhs) { x = rhs; return this; } } Now: A a = new A(); a = 3.14; // ok Then the next question is b.Y = 3, which is one of those cases where it just isn't going to work like that. Y is pretty explicit about *wanting* an A, this isn't setting to an A, this would actually be implicit construction which is generally a no-no in D. However, if you add: property A Y(double v) { return a = v; } to class B, then it will compile, with a = v calling A.opAssign. A cool thing about opAssign that aias this doesn't do right now is it can be templated or you can overload it, and handle a vast variety of types.
Aug 01 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Friday, 2 August 2013 at 02:21:25 UTC, Maxim Fomin wrote:
 On Thursday, 1 August 2013 at 21:56:46 UTC, John Colvin wrote:
 You are asking for an "int" to be be implicitly convertable to 
 an "A", which isn't possible.

http://dpaste.dzfl.pl/c3cce6e2 import std.stdio; class A { double x; property double X() { writeln("From A.Get"); return x; } property double X(double v) { writeln("From A.Set"); x = v; return x; } alias X this; this(double val) { this.x = val; } } class B { A a; A Y() { writeln("From B.Get"); return a; } A Y(A v ...) { writeln("From B.Set"); a = v; return a; } this() { a = new A(0); } } void main() { B b = new B(); writeln(b.Y); b.Y = 3; writeln(b.Y); readln(); }

Ok... It doesn't work for structs though which is strange. The docs do only mention classes however. That's not really an implicit conversion, it's an implicit contstruction. It's definitely not like alias this. (In your example the alias this is completely unused) Does this have any bearing on the wider question? The variadic class construction syntax could be allowed for properties, with a restriction to only one argument passed.
Aug 02 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Friday, 2 August 2013 at 01:55:35 UTC, JS wrote:
 On Friday, 2 August 2013 at 01:46:28 UTC, Jesse Phillips wrote:
 I'm going to provide a reduced case for the issue you are 
 having.

 struct Foo {
    double x;
    alias x this;
 }

 void main() {
    Foo a;
    a = 8; // Alias this assignment
    assert(a.x == 8);
    fun(7); // What you want
 }

 void fun(Foo a) {
 }

 You're claiming that since Foo is requested to "be" a double 
 calling fun(7) should just assign 7 to x.

 What you're missing is that it is Foo which behaves like 
 double, however fun(7) there is no Foo involved, it doesn't 
 exist. You're actually requesting that a Foo is created, then 
 to have an assignment of 7 to x of the new temporary Foo.

 This is not a bug, it is by design. Alias this is sugar of 
 type it is declared in not the type it is declared to.

Um, but I'm not talking about a free function or member function but a property... they are suppose to be special else whats the point. They are suppose to wrap fields. Because of this "design" you speak of, I can't use a double wrapped(essentially) type because it is in an interface and I can't use a field in the interface. What is the difference between class A { int x; } class A { private int _x; property int x() { } property int x(int v) { } } ? From the outside world there is suppose to be no difference.

In an ideal world, perhaps. There has been a huge amount of discussion about properties here before and no solution is perfect.
Aug 02 2013
prev sibling parent "Tommi" <tommitissari hotmail.com> writes:
On Friday, 2 August 2013 at 01:55:35 UTC, JS wrote:
 What is the difference between

 class A
 {
     int x;
 }

 class A
 {
     private int _x;
      property int x() { }
      property int x(int v) { }
 }

 ? From the outside world there is suppose to be no difference.

In the first case other modules can create a mutable pointer to the field x. In the second case other modules cannot create any pointer to field _x.
Aug 02 2013