April 06, 2005
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
April 06, 2005
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
April 06, 2005
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
April 06, 2005
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.
April 06, 2005
> 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.


April 06, 2005
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. 
> 
April 06, 2005
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..
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.

> 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
April 06, 2005
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
April 06, 2005
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
April 06, 2005
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>
>