www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opValue()

reply Derek Parnell <derek psych.ward> writes:
Walter (and anyone else),

AAAARRRRRGGGGGHHHH!!!!! (Sorry about that. I'm getting a little frustrated
at the hoops D is putting me through).

Here's an idea to kick around...

In the construct 

    a = <expression>;

when 'a' is an aggregate, it is equivalent to coding

    a.opValue(<expression>);

This would make my coding life *so* much easier. It is one way of providing
a natural coding style for implicit conversion of classes and structs.

Here is some sample code to explain what I'd like ...
<code>
import std.stdio;
class A
{
    private int aa;
    this() { aa = 2; }
    A opValue(B y)
    {
        aa = y.bb + 1; // 'convert' a B to an A
        return this;
    }
}
class B
{
    private int bb;
    this() { bb = 4; }
    B opValue(A y)
    {
        bb = y.aa - 10;  // 'convert' an A to a B
        return this;
    }
}

void main()
{
    A a = new A;
    B b = new B;

    writefln("Before %d %d", a.aa, b.bb);

    a.opValue(b); // aka: a = b;
    b.opValue(a); // aka: b = a;

    writefln("After  %d %d", a.aa, b.bb);
}
</code>

-- 
Derek Parnell
Melbourne, Australia
http://www.dsource.org/projects/build/ v1.19 released 04/Apr/2005
http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage
6/04/2005 10:45:52 AM
Apr 05 2005
next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Derek Parnell" <derek psych.ward> wrote in message 
news:9wvklcksh1nr.n1xvda56jdpe$.dlg 40tude.net...
 Walter (and anyone else),

 AAAARRRRRGGGGGHHHH!!!!! (Sorry about that. I'm getting a little frustrated
 at the hoops D is putting me through).

no problem.
 Here's an idea to kick around...

 In the construct

    a = <expression>;

 when 'a' is an aggregate, it is equivalent to coding

    a.opValue(<expression>);

 This would make my coding life *so* much easier. It is one way of 
 providing
 a natural coding style for implicit conversion of classes and structs.

I'm confused. By aggregate I assume you mean either a struct or class type. But with a class a.opValue will seg-v if a is null so how does one initialize a? See http://www.digitalmars.com/d/archives/23623.html for a thread including a message from Walter explaining why D doens't allow assignment to be overloaded. More context about what you are actually trying to do would help, too.
 Here is some sample code to explain what I'd like ...
 <code>
 import std.stdio;
 class A
 {
    private int aa;
    this() { aa = 2; }
    A opValue(B y)
    {
        aa = y.bb + 1; // 'convert' a B to an A
        return this;
    }
 }
 class B
 {
    private int bb;
    this() { bb = 4; }
    B opValue(A y)
    {
        bb = y.aa - 10;  // 'convert' an A to a B
        return this;
    }
 }

 void main()
 {
    A a = new A;
    B b = new B;

    writefln("Before %d %d", a.aa, b.bb);

    a.opValue(b); // aka: a = b;
    b.opValue(a); // aka: b = a;

    writefln("After  %d %d", a.aa, b.bb);
 }
 </code>

What's wrong with writing a.opValue(b) or a.copyFrom(b) or something? Why is "=" needed? Are you using templates?
Apr 05 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 5 Apr 2005 21:48:54 -0400, Ben Hinkle wrote:

 "Derek Parnell" <derek psych.ward> wrote in message 
 news:9wvklcksh1nr.n1xvda56jdpe$.dlg 40tude.net...

 Here's an idea to kick around...

 In the construct

    a = <expression>;

 when 'a' is an aggregate, it is equivalent to coding

    a.opValue(<expression>);

 This would make my coding life *so* much easier. It is one way of 
 providing
 a natural coding style for implicit conversion of classes and structs.

I'm confused. By aggregate I assume you mean either a struct or class type.

Yep.
 But with a class a.opValue will seg-v if a is null so how does one 
 initialize a? 

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...
 See http://www.digitalmars.com/d/archives/23623.html for a 
 thread including a message from Walter explaining why D doens't allow 
 assignment to be overloaded.

Yes, I remember this discussion. It was mainly dealing with constructors for structs and deep vs shallow copying. The primary reason (I think) for Walter not liking that, is what happens when assigning class instances between related classes. Eg... class C {} class B:C {} class A:B {} C c; A a; c = a; // This is legal and useful.
 More context about what you are actually trying to do would help, too.

See code below ;-) My emphasis is on making life easier for coders and readers. So sure there is a 'work around', namely explicitly calling opValue(), but I argue that doing so is not as intuitive as the commonly used '=' symbol. So here is a more fuller description of what I think would make coding in D a more pleasant experience. __________________________ In the construct a = <expression>; when 'a' is an aggregate *and* it contains the member function 'opValue', it is equivalent to coding a.opValue(<expression>); otherwise the construct behaves as with the current semantics. __________________________ The idea, is to have a nice way in which a class designer can invent built-in conversion routines for explicit data types, and have people use those conversion routines in a standard, clean and intuitive manner.
 Here is some sample code to explain what I'd like ...
 <code>
 import std.stdio;
 class A
 {
    private int aa;
    this() { aa = 2; }
    A opValue(B y)
    {
        aa = y.bb + 1; // 'convert' a B to an A
        return this;
    }
 }
 class B
 {
    private int bb;
    this() { bb = 4; }
    B opValue(A y)
    {
        bb = y.aa - 10;  // 'convert' an A to a B
        return this;
    }
 }

 void main()
 {
    A a = new A;
    B b = new B;

    writefln("Before %d %d", a.aa, b.bb);

    a.opValue(b); // aka: a = b;
    b.opValue(a); // aka: b = a;

    writefln("After  %d %d", a.aa, b.bb);
 }
 </code>

What's wrong with writing a.opValue(b) or a.copyFrom(b) or something? Why is "=" needed?

To make writing and reading coding a more pleasant experience. Imaging being able to doing something like this ... Circle c; Square s; Triangle t; . . . c = s; // Convert the square to a circle. t = c; // Convert the circle to a triangle. . . . In my current project, I need to convert between various classes and structs, and currently it involves lots of white-noise coding. A cleaner, clearer, method of coding this would be appreciated.
 Are you using templates?

Yes, and that too. That fact that you must use different syntax depending on whether you are using a class, a struct, an array or an intrinsic data type is a real PITA. -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build/ v1.19 released 04/Apr/2005 http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage 6/04/2005 12:54:43 PM
Apr 05 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward> wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed. Regan.
Apr 05 2005
next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Regan Heath" <regan netwin.co.nz> wrote in message 
news:opsosivfu423k2f5 nrage.netwin.co.nz...
 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward> wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed. Regan.

Yes, that's what I was thinking. To put it another way, the code A a; will initialize a to null and so when you try to run a = b; it will try to run a.opValue(b) which will seg-v. This is slightly different than A a = new A; but basically I'm wondering how a.opValue works when a is null or during initialization.
Apr 05 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Tue, 5 Apr 2005 23:17:53 -0400, Ben Hinkle wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message 
 news:opsosivfu423k2f5 nrage.netwin.co.nz...
 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward> wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed. Regan.

Yes, that's what I was thinking. To put it another way, the code A a; will initialize a to null and so when you try to run a = b; it will try to run a.opValue(b) which will seg-v. This is slightly different than A a = new A; but basically I'm wondering how a.opValue works when a is null or during initialization.

In my way of thinking, when a class is initialized it calls the constructor routine 'this' and so it wouldn't attempt calling opValue. Also, if the class instance is not initialized (ie still null), then opValue couldn't be called. Why are you guys thinking that I'm talking about initialization? I'm just trying to invent a nice way to implement customized data conversions. That is, converting data from an evaluated expression to an instance of a class/struct. class C { C opValue(int x) { . . . return this; } } . . . C myC = new C; . . . myC = 42; // i.e. myC.opValue(42); -- Derek Melbourne, Australia 6/04/2005 2:03:28 PM
Apr 05 2005
next sibling parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 6 Apr 2005 14:09:43 +1000, Derek Parnell <derek psych.ward> wrote:
 On Tue, 5 Apr 2005 23:17:53 -0400, Ben Hinkle wrote:

 "Regan Heath" <regan netwin.co.nz> wrote in message
 news:opsosivfu423k2f5 nrage.netwin.co.nz...
 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward>  
 wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed. Regan.

Yes, that's what I was thinking. To put it another way, the code A a; will initialize a to null and so when you try to run a = b; it will try to run a.opValue(b) which will seg-v. This is slightly different than A a = new A; but basically I'm wondering how a.opValue works when a is null or during initialization.

In my way of thinking, when a class is initialized it calls the constructor routine 'this' and so it wouldn't attempt calling opValue. Also, if the class instance is not initialized (ie still null), then opValue couldn't be called. Why are you guys thinking that I'm talking about initialization?

Because as a general rule what you're suggesting appeared to me to effect initialization (after I read Bens response). So, I wanted clarification as to how you thought it would work with initialization, which you've just given above. Some comments:
 In my way of thinking, when a class is initialized it calls the  
 constructor
 routine 'this' and so it wouldn't attempt calling opValue.

My take on this, from my other post: <quote> Well I'm no expert on how this works, but I imagine... A a = new A(); RHS: "new A()" means allocate some memory on the heap, and calls "this" for A in it. <at this stage we have an A class in heap memory assigned to nothing) LHS: "A a" <at this stage we have a null reference to an A on the stack called 'a'> "=" means assign RHS to LHS. <the A in memory is assigned to the 'a' on the stack> </quote>
 Also, if the
 class instance is not initialized (ie still null), then opValue couldn't  
 be
 called.

Correct, so what happens? Isn't this like saying: if (a == b) when a is a null reference? which casues a seg-v. Regan
Apr 05 2005
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Wed, 6 Apr 2005 14:09:43 +1000, Derek Parnell wrote:

 On Tue, 5 Apr 2005 23:17:53 -0400, Ben Hinkle wrote:
 
 "Regan Heath" <regan netwin.co.nz> wrote in message 
 news:opsosivfu423k2f5 nrage.netwin.co.nz...
 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward> wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed. Regan.

Yes, that's what I was thinking. To put it another way, the code A a; will initialize a to null and so when you try to run a = b; it will try to run a.opValue(b) which will seg-v. This is slightly different than A a = new A; but basically I'm wondering how a.opValue works when a is null or during initialization.

In my way of thinking, when a class is initialized it calls the constructor routine 'this' and so it wouldn't attempt calling opValue. Also, if the class instance is not initialized (ie still null), then opValue couldn't be called. Why are you guys thinking that I'm talking about initialization? I'm just trying to invent a nice way to implement customized data conversions. That is, converting data from an evaluated expression to an instance of a class/struct. class C { C opValue(int x) { . . . return this; } } . . . C myC = new C; . . . myC = 42; // i.e. myC.opValue(42);

We might actually need an opValue_r() routine too. void opValue_r(inout int x) { . . . x = ... ; } . . . int y; y = myC; // i.e. myC.opValue_r(y); And if you are concerned by opValue_r not returning anything, (damn C's heritage) you can still do " while ( (y = myC, y) > 0) ... " :D [offtopic: Q. How much of B and BPCL did Richie & Kernighan jettison? A. Enough. Q. How much of C and C++ did Walters jettison? A. Not enough (yet?) ] -- Derek Melbourne, Australia 6/04/2005 2:12:00 PM
Apr 05 2005
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 06 Apr 2005 15:05:29 +1200, Regan Heath wrote:

 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward> wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed.

Why? Doesn't the 'new' keyword mean 'call the this() member function'? -- Derek Melbourne, Australia 6/04/2005 1:25:31 PM
Apr 05 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 6 Apr 2005 13:26:15 +1000, Derek Parnell <derek psych.ward> wrote:
 On Wed, 06 Apr 2005 15:05:29 +1200, Regan Heath wrote:

 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward>  
 wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed.

Why? Doesn't the 'new' keyword mean 'call the this() member function'?

Well I'm no expert on how this works, but I imagine... A a = new A(); RHS: "new A()" means allocate some memory on the heap, and calls "this" for A in it. <at this stage we have an A class in heap memory assigned to nothing) LHS: "A a" <at this stage we have a null reference to an A on the stack called 'a'> "=" means assign RHS to LHS. <the A in memory is assigned to the 'a' on the stack> Ben's other question is perhaps more important. A a; a = b; <- a is null, this calls a.opValue(b). Does that seg-v or.. ? Regan
Apr 05 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 06 Apr 2005 16:16:05 +1200, Regan Heath wrote:

 On Wed, 6 Apr 2005 13:26:15 +1000, Derek Parnell <derek psych.ward> wrote:
 On Wed, 06 Apr 2005 15:05:29 +1200, Regan Heath wrote:

 On Wed, 6 Apr 2005 12:57:00 +1000, Derek Parnell <derek psych.ward>  
 wrote:
 But with a class a.opValue will seg-v if a is null so how does one
 initialize a?

Huh? Did I mention initializing? See example code below. Classes would still be initialized as normal, with the 'new' keyword. I'm talking about assigning value to a (existing) class/struct based on the value of an expression. Nothing to do with constructors, etc...

I think what Ben was trying to say here is that: (assume A is a class) A a = new A(); *is* assigning a value to 'a' so would call "opValue(A a)" in A, if it existed.

Why? Doesn't the 'new' keyword mean 'call the this() member function'?

Well I'm no expert on how this works, but I imagine... A a = new A(); RHS: "new A()" means allocate some memory on the heap, and calls "this" for A in it. <at this stage we have an A class in heap memory assigned to nothing)

Yep.
 LHS: "A a"
 <at this stage we have a null reference to an A on the stack called 'a'>

Yep.
 "=" means assign RHS to LHS.
 <the A in memory is assigned to the 'a' on the stack>

Sort of. The "= new" means that 'a' is updated to point to the new A on the heap. I just saying that "a = new <CLASS>" creates a new instance (just like it does now), and "a = <expression>" calls a.opValue(<expression>) if 'a' is already instantiated *and* opValue(typeid(<expression>)) exists as a member function of a. So obviously "A a = new A" could never call "a.opValue(A)" because 'a' is not instantiated yet. But ... A a = new A; A b = new A; a = b; // This could call a.opValue(b) if one was defined. This would be a neat one to customize the simple reference-copy semantics, if one needed to do that.
 Ben's other question is perhaps more important.
 
 A a;
 a = b; <- a is null, this calls a.opValue(b).
 
 Does that seg-v or.. ?

seg-v, of course. This is normal behaviour and need not change. -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build/ v1.19 released 04/Apr/2005 http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage 6/04/2005 2:31:47 PM
Apr 05 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 6 Apr 2005 15:02:46 +1000, Derek Parnell <derek psych.ward> wrote:
 On Wed, 06 Apr 2005 16:16:05 +1200, Regan Heath wrote:
 Well I'm no expert on how this works, but I imagine...

 A a = new A();

 RHS: "new A()" means allocate some memory on the heap, and calls "this"
 for A in it.
 <at this stage we have an A class in heap memory assigned to nothing)

Yep.
 LHS: "A a"
 <at this stage we have a null reference to an A on the stack called 'a'>

Yep.
 "=" means assign RHS to LHS.
 <the A in memory is assigned to the 'a' on the stack>

Sort of. The "= new" means that 'a' is updated to point to the new A on the heap.

That's what I said above only I broke it into the 3 steps it takes. This last step simply assigns the reference created by "new A()" to the reference created on the stack by "A a". In other words this last step is identical to the step performed by "=" in "a = b", the only difference here is that "b" is another stack reference and not a "new" one.
 I just saying that "a = new <CLASS>" creates a new instance (just like it
 does now), and "a = <expression>" calls a.opValue(<expression>) if 'a' is
 already instantiated *and* opValue(typeid(<expression>)) exists as a  
 member function of a.

But... "a = new <CLASS>" is technically "a = <expression>" as "new" is an expression: http://www.digitalmars.com/d/expression.html#NewExpression So "a = new A()" could call "a.opValue(new A())" So you see why Ben and I have been asking about it? Regan
Apr 05 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 06 Apr 2005 17:38:26 +1200, Regan Heath wrote:

[snip]

 But... "a = new <CLASS>" is technically "a = <expression>" as "new" is an  
 expression:
    http://www.digitalmars.com/d/expression.html#NewExpression
 
 So "a = new A()" could call "a.opValue(new A())"
 
 So you see why Ben and I have been asking about it?

Yes. It seems that I've (once again) been unable to express myself clearly though. Essentially I'm talking about a change in D's behaviour. I can see that the way D *currently* interprets the syntax, it would not achieve what I'd like to see happen. So, I talk of change. This proposed change to D would not break existing code either, because any code like this is already broken (one gets the 'cannot implicitly cast' message). I'm proposing that instead of automatically getting that message, D checks to see if it can call the appropriate opValue() member function. If it can't then if falls back to the 'implicit cast' error. This means that in my envisioned D, (i.e. not the current one), then syntax in the form ... <CLASS> <IDENT> = new <CLASS> [ ARGLIST ]; be interpreted as *just* an instantiation of the class, and thus this form of 'assignment' would not check for opValue() members. Is that really too much to ask for? Is is really stupid of me to ask for a standard mechanism to enable coders to provide (implicit) casting methods that convert an expression into a form suitable to update a class or struct instance ( or any type would be nice too)? I was hoping to avoid a new operator as those seem harder to implement and usually meet more resistance for a syntax faux pas. So I thought that hijacking something that is currently an error construct, and turning it into something that we could use instead, would be a 'Good Thing'. <AGGREGATEINSTANCE> = <NON_NEW_EXPRESSION> would be interpreted as <AGGREGATEINSTANCE>.opValue( <NON_NEW_EXPRESSION> ) if has a member function opValue( TypeOf (<NON_NEW_EXPRESSION>) ) defined. If this criteria was not present, then the current semantics would apply. Of course, if the instance was null at the time of calling, then the usual run time error would apply. -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build/ v1.19 released 04/Apr/2005 http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage 6/04/2005 5:02:41 PM
Apr 06 2005
next sibling parent Georg Wrede <georg.wrede nospam.org> writes:
Derek Parnell wrote:
 On Wed, 06 Apr 2005 17:38:26 +1200, Regan Heath wrote:
 
 [snip]
 
 
But... "a = new <CLASS>" is technically "a = <expression>" as "new" is an  
expression:
   http://www.digitalmars.com/d/expression.html#NewExpression

So "a = new A()" could call "a.opValue(new A())"

So you see why Ben and I have been asking about it?

Yes. It seems that I've (once again) been unable to express myself clearly though. Essentially I'm talking about a change in D's behaviour. I can see that the way D *currently* interprets the syntax, it would not achieve what I'd like to see happen. So, I talk of change. This proposed change to D would not break existing code either, because any code like this is already broken (one gets the 'cannot implicitly cast' message). I'm proposing that instead of automatically getting that message, D checks to see if it can call the appropriate opValue() member function. If it can't then if falls back to the 'implicit cast' error. This means that in my envisioned D, (i.e. not the current one), then syntax in the form ... <CLASS> <IDENT> = new <CLASS> [ ARGLIST ]; be interpreted as *just* an instantiation of the class, and thus this form of 'assignment' would not check for opValue() members. Is that really too much to ask for? Is is really stupid of me to ask for a standard mechanism to enable coders to provide (implicit) casting methods that convert an expression into a form suitable to update a class or struct instance ( or any type would be nice too)? I was hoping to avoid a new operator as those seem harder to implement and usually meet more resistance for a syntax faux pas. So I thought that hijacking something that is currently an error construct, and turning it into something that we could use instead, would be a 'Good Thing'. <AGGREGATEINSTANCE> = <NON_NEW_EXPRESSION> would be interpreted as <AGGREGATEINSTANCE>.opValue( <NON_NEW_EXPRESSION> ) if has a member function opValue( TypeOf (<NON_NEW_EXPRESSION>) ) defined. If this criteria was not present, then the current semantics would apply. Of course, if the instance was null at the time of calling, then the usual run time error would apply.

Id vote for this.
Apr 06 2005
prev sibling parent reply xs0 <xs0 xs0.com> writes:
What about

Obj a = ObjFactory.create();

This is quite a common pattern, if you want all the newing of something 
to happen in one place (so you can easily switch the implementation). 
Having it your way would destroy this possibility, driving all OO-heads 
away (me too, I guess :)

What about

Obj a = cond ? new Something() : oldSth;

This is not a NewExpression..

What you're asking is not very feasible, I'm afraid. I think you have 
better chances by trying for a new operator..

BTW, why opValue() not opSet()?


xs0




Derek Parnell wrote:
 On Wed, 06 Apr 2005 17:38:26 +1200, Regan Heath wrote:
 
 [snip]
 
 
But... "a = new <CLASS>" is technically "a = <expression>" as "new" is an  
expression:
   http://www.digitalmars.com/d/expression.html#NewExpression

So "a = new A()" could call "a.opValue(new A())"

So you see why Ben and I have been asking about it?

Yes. It seems that I've (once again) been unable to express myself clearly though. Essentially I'm talking about a change in D's behaviour. I can see that the way D *currently* interprets the syntax, it would not achieve what I'd like to see happen. So, I talk of change. This proposed change to D would not break existing code either, because any code like this is already broken (one gets the 'cannot implicitly cast' message). I'm proposing that instead of automatically getting that message, D checks to see if it can call the appropriate opValue() member function. If it can't then if falls back to the 'implicit cast' error. This means that in my envisioned D, (i.e. not the current one), then syntax in the form ... <CLASS> <IDENT> = new <CLASS> [ ARGLIST ]; be interpreted as *just* an instantiation of the class, and thus this form of 'assignment' would not check for opValue() members. Is that really too much to ask for? Is is really stupid of me to ask for a standard mechanism to enable coders to provide (implicit) casting methods that convert an expression into a form suitable to update a class or struct instance ( or any type would be nice too)? I was hoping to avoid a new operator as those seem harder to implement and usually meet more resistance for a syntax faux pas. So I thought that hijacking something that is currently an error construct, and turning it into something that we could use instead, would be a 'Good Thing'. <AGGREGATEINSTANCE> = <NON_NEW_EXPRESSION> would be interpreted as <AGGREGATEINSTANCE>.opValue( <NON_NEW_EXPRESSION> ) if has a member function opValue( TypeOf (<NON_NEW_EXPRESSION>) ) defined. If this criteria was not present, then the current semantics would apply. Of course, if the instance was null at the time of calling, then the usual run time error would apply.

Apr 06 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Wed, 06 Apr 2005 18:07:25 +0200, xs0 wrote:

 What about
 
 Obj a = ObjFactory.create();
 
 This is quite a common pattern, if you want all the newing of something 
 to happen in one place (so you can easily switch the implementation). 
 Having it your way would destroy this possibility, driving all OO-heads 
 away (me too, I guess :)

I'm not sure. I guess you wouldn't have a member function of Obj like "opValue(Obj x)" because that is like saying, 'here is how you convert an Obj to an Obj'. Doesn't make any sense.
 What about
 
 Obj a = cond ? new Something() : oldSth;
 
 This is not a NewExpression..

construct that will give you the 'cannot implicitly cast' error. It is *those* constructs I was aiming at doing something with.
 What you're asking is not very feasible, I'm afraid. I think you have 
 better chances by trying for a new operator..

But the concept of wanting to be able to let the compile know how to convert one class into another is still a good one, no? If it's just a syntax issue remaining, I'm sure we can get around that somehow.
 BTW, why opValue() not opSet()?

Dunno. I guess I see it as "The Value of X comes from <Expression>". So I'm focusing on the noun rather than the verb. It doesn't really matter to me. -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build 7/04/2005 2:19:05 AM
Apr 06 2005
parent reply xs0 <xs0 xs0.com> writes:
Derek Parnell wrote:
 On Wed, 06 Apr 2005 18:07:25 +0200, xs0 wrote:
 
 
What about

Obj a = ObjFactory.create();

This is quite a common pattern, if you want all the newing of something 
to happen in one place (so you can easily switch the implementation). 
Having it your way would destroy this possibility, driving all OO-heads 
away (me too, I guess :)

I'm not sure. I guess you wouldn't have a member function of Obj like "opValue(Obj x)" because that is like saying, 'here is how you convert an Obj to an Obj'. Doesn't make any sense.

OK, but what if it is FooFactory? If there was opValue() it would seem completely logical, yet it would fail..
Obj a = cond ? new Something() : oldSth;

This is not a NewExpression..

True. However, if it is legal in your application, then it's also not a construct that will give you the 'cannot implicitly cast' error. It is *those* constructs I was aiming at doing something with.

It's not legal now, but with opValue it would be legal, because you could assign both new Something() or oldSth to a. Now, although that looks normal, it will crash if oldSth or Something are not implicitly castable, but only opValue() castable. I mean, you want to override reference assignment, which either compiles and always works or doesn't compile in the first place, with a function call which may or may not work in runtime..
What you're asking is not very feasible, I'm afraid. I think you have 
better chances by trying for a new operator..

But the concept of wanting to be able to let the compile know how to convert one class into another is still a good one, no? If it's just a syntax issue remaining, I'm sure we can get around that somehow.

Well, it is and it isn't.. I'd prefer multiple opCasts, so it's immediately obvious what you're doing.. And isn't this good enough for a shorter syntax: class T { T opShlAssign(Foo c) { /*...*/ return this; } } a<<=b; // almost like a=b, if you ask me I guess in 99.99999% cases, shifting an object to left with another object doesn't make sense anyway, so the operator might as well get another use.. xs0
Apr 06 2005
parent reply brad domain.invalid writes:
rter syntax:
 
 class T { T opShlAssign(Foo c) { /*...*/ return this; } }
 
 a<<=b; // almost like a=b, if you ask me
 
 I guess in 99.99999% cases, shifting an object to left with another 
 object doesn't make sense anyway, so the operator might as well get 
 another use..
 
 
 xs0

Argh! Surely not! I thought one of the philosophies of D was that operators should _always_ do what you expect. The <<= operator should always shift left & assign. Derek - am I right in saying that you want an assignment operator that doesn't create a new object, but instead alters an existing one? At the moment, when you deal with objects/references, the = operator always means "set this reference to this object". I don't think that it makes sense to overload this meaning, because you then end up with an operator that in some cases does "set this reference to this object" and in other cases "alter the internal data layout of object A, based on the values in object B" And then you end up with a whole lot of special rules to try and remember which assignment operator is applying. Overwriting the contents of an object when you really ment to assign to a new object could ruin your day if you hold multiple references to the object you are stomping on. I am in favour of having an operator that means "take object B, get some values out of it and setup object A accordingly" Something like Foo a = new Foo (); Bar b = new Bar (); a <- b // which calls a.opConvert(b); I guess you could also end up with nasty a = (new Foo())<- b; <- is probably not the operator to choose, probably hard to parse. Maybe someone else can think of a good operator if this functionality is required. Brad
Apr 06 2005
next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 07 Apr 2005 08:45:21 +1200, <brad domain.invalid> wrote:
 rter syntax:
  class T { T opShlAssign(Foo c) { /*...*/ return this; } }
  a<<=b; // almost like a=b, if you ask me
  I guess in 99.99999% cases, shifting an object to left with another  
 object doesn't make sense anyway, so the operator might as well get  
 another use..
   xs0

Argh! Surely not! I thought one of the philosophies of D was that operators should _always_ do what you expect. The <<= operator should always shift left & assign. Derek - am I right in saying that you want an assignment operator that doesn't create a new object, but instead alters an existing one? At the moment, when you deal with objects/references, the = operator always means "set this reference to this object". I don't think that it makes sense to overload this meaning, because you then end up with an operator that in some cases does "set this reference to this object" and in other cases "alter the internal data layout of object A, based on the values in object B" And then you end up with a whole lot of special rules to try and remember which assignment operator is applying. Overwriting the contents of an object when you really ment to assign to a new object could ruin your day if you hold multiple references to the object you are stomping on. I am in favour of having an operator that means "take object B, get some values out of it and setup object A accordingly"

And if they're the same type, it could be used for a "deep-copy"? I think we can combine this casting concern with the deep-copy concern, they seem closely related to me.
 Something like
 Foo a = new Foo ();
 Bar b = new Bar ();
 a <- b // which calls a.opConvert(b);

 I guess you could also end up with nasty
 a = (new Foo())<- b;

It seems 'correct' if 'ugly', done in 2 steps looks nicer and incurs no additional penalty (that I can see). a = new Foo(); a <- b; this operator is really a "copy RHS into LHS" operator, which implys that LHS must exists beforehand. Or using the "<-" operator could implicitly call new if the LHS is null... not sure how feasible that is.
 <- is probably not the operator to choose, probably hard to parse. Maybe  
 someone else can think of a good operator if this functionality is  
 required.

I agree, I think the main concern against this in Walters case was that implicit conversion when "=" is used can cause subtle behaviour and bugs. (correct me if I am wrong). So, if a new operator was used, eg. ":=" or something then this concern vanishes, as "=" does what it currently does: - shallow copy for arrays. - reference assignment for classes etc - deep copy for value types The new ":=" operator always means "copy RHS into LHS" AKA: - deep copy for arrays (provided items in array have deep-copy method defined) - deep copy for classes (provided class has deep-copy method defined) - same as "=" for value types. Thoughts? Regan
Apr 06 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 07 Apr 2005 13:34:52 +1200, Regan Heath wrote:

[snip]
 
 So, if a new operator was used, eg. ":=" or something then this concern  
 vanishes, as "=" does what it currently does:
   - shallow copy for arrays.
   - reference assignment for classes etc
   - deep copy for value types
 
 The new ":=" operator always means "copy RHS into LHS" AKA:
   - deep copy for arrays (provided items in array have deep-copy method  
 defined)
   - deep copy for classes (provided class has deep-copy method defined)
   - same as "=" for value types.
 
 Thoughts?

Ok, an additional operator then. So long as 'value types' also includes the intrinsic data types like int, real, byte, ..., so that we could make useful templates, I could cope with this. Thus ... int a; a = b; and a := b; would behave identically. And, we still need to deal with ... struct Foo {} int a; Foo b; a := b; Maybe Walter will allow us to do ... void opDeepCopy(inout int X, Foo Y) { ... } so that " a := b; " would call opDeepCopy(a,b); Also, I'd like to be able to do deep copies for structs too, even though they are 'value' types. Such that ... struct Foo {} Foo a; a := b; // call a.opDeepCopy(b) or some such. -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build/ v1.19 released 04/Apr/2005 http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage 7/04/2005 12:06:34 PM
Apr 06 2005
parent reply brad domain.invalid writes:
 Ok, an additional operator then. So long as 'value types' also includes the
 intrinsic data types like int, real, byte, ..., so that we could make
 useful templates, I could cope with this. 
 Thus ...
 
    int a;
 
    a = b;
 and
    a := b;
 
 would behave identically.
 
 And, we still need to deal with ...
 
    struct Foo {}
    int a;
    Foo b;
 
    a := b;
 
 Maybe Walter will allow us to do ...
 
    void opDeepCopy(inout int X, Foo Y) { ... }
 
 so that " a := b; " would call
 
    opDeepCopy(a,b);
 
 Also, I'd like to be able to do deep copies for structs too, even though
 they are 'value' types. Such that ...
 
    struct Foo {}
    Foo a;
 
    a := b;  // call a.opDeepCopy(b) or some such.
 

1) The main point of := is so we can write templated functions that behave well with intrinsics and user defined types, without needing hacks to get them to work. 2) This won't break any new code, and will allow us to be more expressive with D. 3) ":=" represents "deep copy/convert", it may call a function and should be reguarded as a potentially expensive operator 4) ":=" will fail if the LHS is null (or should it resolve to a LHS static opDeepCopy(inout LHS_class x, type RHS) ?) 5) ":=" with intrinsic LHS will call the function opDeepCopy(LHS, RHS) 6) Have two "assignment" operators is potentially confusing - but probably no more than understanding the difference between shallow and deep copy. 7) we not longer need the .dup property?, because we can go int a[]; int b[]; a := b; // which is a deep copy of b into a Brad
Apr 06 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Thu, 07 Apr 2005 14:17:54 +1200, <brad domain.invalid> wrote:
 Ok, an additional operator then. So long as 'value types' also includes  
 the
 intrinsic data types like int, real, byte, ..., so that we could make
 useful templates, I could cope with this. Thus ...
     int a;
     a = b;
 and
    a := b;
  would behave identically.
  And, we still need to deal with ...
     struct Foo {}
    int a;
    Foo b;
     a := b;
  Maybe Walter will allow us to do ...
     void opDeepCopy(inout int X, Foo Y) { ... }
  so that " a := b; " would call
     opDeepCopy(a,b);
  Also, I'd like to be able to do deep copies for structs too, even  
 though
 they are 'value' types. Such that ...
     struct Foo {}
    Foo a;
     a := b;  // call a.opDeepCopy(b) or some such.

1) The main point of := is so we can write templated functions that behave well with intrinsics and user defined types, without needing hacks to get them to work. 2) This won't break any new code, and will allow us to be more expressive with D. 3) ":=" represents "deep copy/convert", it may call a function and should be reguarded as a potentially expensive operator 4) ":=" will fail if the LHS is null (or should it resolve to a LHS static opDeepCopy(inout LHS_class x, type RHS) ?) 5) ":=" with intrinsic LHS will call the function opDeepCopy(LHS, RHS) 6) Have two "assignment" operators is potentially confusing - but probably no more than understanding the difference between shallow and deep copy. 7) we not longer need the .dup property?, because we can go int a[]; int b[]; a := b; // which is a deep copy of b into a

This seems a fairly good list of the points so far, plus a few new/reworded ones which I also like. Regan
Apr 07 2005
prev sibling parent reply xs0 <xs0 xs0.com> writes:
brad domain.invalid wrote:
 rter syntax:
 
 class T { T opShlAssign(Foo c) { /*...*/ return this; } }

 a<<=b; // almost like a=b, if you ask me

 I guess in 99.99999% cases, shifting an object to left with another 
 object doesn't make sense anyway, so the operator might as well get 
 another use..


 xs0

Argh! Surely not! I thought one of the philosophies of D was that operators should _always_ do what you expect. The <<= operator should always shift left & assign.

Hmm, that philosophy is good and all, but what would you expect to get from shifting ClassA by ClassB? I just chose this operator because it looks as a left arrow (at least to me), which is exactly what you proposed, and I couldn't think of any other use for it with classes/structs.. <- is no good, though: "a<-3" should be "a < -3", not "a.opConvert(3)" As for a new operator for this functionality, I don't think it's necessary. a.set(b) is short enough, it is also much more obvious what's happening, and it's also obvious that a can't be null.. if you don't use opCall for other stuff, you can even just use a(b).. xs0
Apr 07 2005
parent brad domain.invalid writes:
 Hmm, that philosophy is good and all, but what would you expect to get 
 from shifting ClassA by ClassB? I just chose this operator because it 
 looks as a left arrow (at least to me), which is exactly what you 
 proposed, and I couldn't think of any other use for it with 
 classes/structs..
 
 <- is no good, though: "a<-3" should be "a < -3", not "a.opConvert(3)"
 
 As for a new operator for this functionality, I don't think it's 
 necessary. a.set(b) is short enough, it is also much more obvious what's 
 happening, and it's also obvious that a can't be null.. if you don't use 
 opCall for other stuff, you can even just use a(b)..
 
 
 xs0

Well, to be honest I don't know what to expect from <<= if you override it and make it do things that aren't shift left and assign :) What if my LHS class is an implementation that can be shifted left? Anyhow - I think the main reason for having an assignment operator instead of a.set(b), is that for templates you want the template to work for classes and built in primitives. Brad
Apr 07 2005
prev sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
 Imaging being able to doing something like this ...

  Circle c;
  Square s;
  Triangle t;
  . . .
  c = s; // Convert the square to a circle.
  t = c; // Convert the circle to a triangle.
  . . .

 In my current project, I need to convert between various classes and
 structs, and currently it involves lots of white-noise coding. A cleaner,
 clearer, method of coding this would be appreciated.

I would recommend you use inheritance. If your code involves lots of casting then the inheritance tree probably needs refactoring. As you've written your code above and commented in other parts of this thread the assignment c = s; t = c; will seg-v. Having been coding in Java for a while it's very nice to know exactly what assignment does. It is very simple and it is always the same. No more unexpected surprises with = that can happen in C++. That's actually why I like to overload as little as possible (eg I have grown to avoid static opCast overloads).
 Are you using templates?

Yes, and that too. That fact that you must use different syntax depending on whether you are using a class, a struct, an array or an intrinsic data type is a real PITA.

To my mind this is the only reason to request some language changes - to make a single template easier to use with both primitives and aggregates.
Apr 06 2005
prev sibling next sibling parent reply David Medlock <ashleymedlock no.spam.yahoo.com> writes:
Overloading assignment is a semantic minefield to say the least, and has 
come up here before (Google this newsgroup).


Redefining the assignment operator would cripple vectorization efforts, 
judging by the recent move towards 'static-single-assignment', such as 
in GCC 4.x, also SAC( single assignment C).

Since assignment might mean assignment and other times mean a method 
call, its hard to convert it to an intermediate SSA representation.

Two viable options :

- Use the overloaded opCall() which takes different types depending on 
what you need.
- Convince Walter to allow a := operator which would throw NullPointer 
if the recipient is null or not a variable.


-David

Derek Parnell wrote:
 Walter (and anyone else),
 
 AAAARRRRRGGGGGHHHH!!!!! (Sorry about that. I'm getting a little frustrated
 at the hoops D is putting me through).
 
 Here's an idea to kick around...
 
 In the construct 
 
     a = <expression>;
 
 when 'a' is an aggregate, it is equivalent to coding
 
     a.opValue(<expression>);
 
 This would make my coding life *so* much easier. It is one way of providing
 a natural coding style for implicit conversion of classes and structs.
 
 Here is some sample code to explain what I'd like ...
 <code>
 import std.stdio;
 class A
 {
     private int aa;
     this() { aa = 2; }
     A opValue(B y)
     {
         aa = y.bb + 1; // 'convert' a B to an A
         return this;
     }
 }
 class B
 {
     private int bb;
     this() { bb = 4; }
     B opValue(A y)
     {
         bb = y.aa - 10;  // 'convert' an A to a B
         return this;
     }
 }
 
 void main()
 {
     A a = new A;
     B b = new B;
 
     writefln("Before %d %d", a.aa, b.bb);
 
     a.opValue(b); // aka: a = b;
     b.opValue(a); // aka: b = a;
 
     writefln("After  %d %d", a.aa, b.bb);
 }
 </code>
 

Apr 06 2005
parent Derek Parnell <derek psych.ward> writes:
On Wed, 06 Apr 2005 16:57:02 -0400, David Medlock wrote:

 Overloading assignment is a semantic minefield to say the least, and has 
 come up here before (Google this newsgroup).
 
 
 Redefining the assignment operator would cripple vectorization efforts, 
 judging by the recent move towards 'static-single-assignment', such as 
 in GCC 4.x, also SAC( single assignment C).
 
 Since assignment might mean assignment and other times mean a method 
 call, its hard to convert it to an intermediate SSA representation.
 
 Two viable options :
 
 - Use the overloaded opCall() which takes different types depending on 
 what you need.
 - Convince Walter to allow a := operator which would throw NullPointer 
 if the recipient is null or not a variable.

Currently, I have this concept implemented as a property of the 'receiving' class. eg. class Foo { void Value( Bar x) { . . . } } Foo a = new Foo; Bar b = new Bar; . . . a.Value = b; However, I have two problems with this. The first problem is that I occasionally forget that I'm dealing with classes and code " a = b ", because it feels natural to code that when transferring value from one thing to another, but I get rewarded with an error message about implicit casting. Secondly, it makes designing a single template that covers all data types impossible. I need to have one template for classes and another for intrinsic types, simply because I can't use "a = b" for both situations. Thank you all for this enlightening discussion. I've learned lots from it, but I can't actually see Walter doing anything about this, even in v2.0, so I'll drop it and blunder on. -- Derek Parnell Melbourne, Australia 7/04/2005 7:21:18 AM
Apr 06 2005
prev sibling parent Vladimir <kv11111 mail.ru> writes:
I like the idea about := operator.

May be one can introduce ":" (opColon) binary operator which in expression
a : b evaluates a and b and then returns b.
Then (using general rule about "op=") ":=" (opColonAssign) will mean
deep-copy and be overloadable ?

-- 
          Vladimir
Apr 07 2005