Thread overview
Suggestion: wrapping up value type objects
Oct 25, 2006
Kristian
Oct 25, 2006
Don Clugston
Oct 25, 2006
Kristian
Oct 25, 2006
Don Clugston
Oct 25, 2006
Kristian
Oct 26, 2006
Kristian
October 25, 2006
I found it strange that D has no copy/clone operator, lets say 'opClone'.

Before you say that there is a reason for it and D does not handle objects as value types (like C++ does), I have to point out that D already handles (or should handle) objects as value types in some situations. I'm talking about the 'opXxx' operators ('opAdd', 'opSub', etc).

First I have to say that I'm not against using objects as reference types. But, sometimes it's necessary to create value types (out of classes), and using them could be easier than it's now.


If a class has an 'opXxx' operator, then it normally returns a temporary object. Also the following should be true: 'a <op>= b' <-> 'a = a <op> b'. (Otherwise it would be a very rare case.)

Dublication is needed when creating a temporary object (in 'opXxx'). So, why don't add the 'opClone' operator to the language? I mean, there already are 'opXxx' functions. Why don't standardize the way of creating copies/clones?


Then you could get rid of all the 'opXxx' and 'opXxx_r' functions: they can be expressed by using 'opClone' and 'opXxxAssign' only. That would also remove any unnecessary temporary objects from statements making calculations very efficient (as stated earlier). For example:

    a = (b * c) + d;
->
    a = b.opClone().opMulAssign(c).opAddAssign(d);

And as mentioned in some earlier posts, you could also have binary and unary clone operators. E.g. (I use '<-' here):

    a <- b;
->
    a = b.opClone();


The 'opClone()' function can also have a default implementation (done by the compiler). All the member variables are dublicated by default. For example:

    class Foo {
        int a;
        Bar b;
    }

The default 'opClone' would then be (generated by the compiler):

    Foo opClone() {
        Foo ret = new Foo;
        ret.a = a;
        ret.b = b.opClone();
        return(ret);
    }

If that's not wanted, or possible, then you can always implement the function by yourself, of course.


Finally, there could also be easier way to create local objects (they too are discussed earlier in various posts). For example:

    Foo obj();  //a local object, already constructed


Benefits:
- There is a standard way to dublicate objects, making programs more consistent, easier to read/write, and (thus) bug free.
- The number of operator functions are reduced almost to one third! (No need to implement 'opXxx's and 'opXxx_r's.)
- No more unnecessary temporaries.
- Value type objects are indeed part of the language. Note that they don't take anything away from normal reference types.
October 25, 2006
Kristian wrote:
> 
> I found it strange that D has no copy/clone operator, lets say 'opClone'.
> 
> Before you say that there is a reason for it and D does not handle objects as value types (like C++ does), I have to point out that D already handles (or should handle) objects as value types in some situations. I'm talking about the 'opXxx' operators ('opAdd', 'opSub', etc).
> 
> First I have to say that I'm not against using objects as reference types. But, sometimes it's necessary to create value types (out of classes), and using them could be easier than it's now.
> 
> 
> If a class has an 'opXxx' operator, then it normally returns a temporary object. Also the following should be true: 'a <op>= b' <-> 'a = a <op> b'. (Otherwise it would be a very rare case.)
> 
> Dublication is needed when creating a temporary object (in 'opXxx'). So, why don't add the 'opClone' operator to the language? I mean, there already are 'opXxx' functions. Why don't standardize the way of creating copies/clones?
> 
> 
> Then you could get rid of all the 'opXxx' and 'opXxx_r' functions: they can be expressed by using 'opClone' and 'opXxxAssign' only.

Not necessarily. Consider Vector and Matrix.

Matrix a;
Matrix b;
Vector c;
a  = b * c;
a = b.opClone().opMulAssign(c); // OK.

But
a = c * b;
a = c.opClone().opMulAssign(b);
// Oops -- we cloned the vector, should have cloned the matrix instead.

In fact, in general you cannot even assume that
(typeof(a*b) == typeof(a)) |  (typeof(a*b) == typeof(b))

which means that cloning would sometimes create useless temporary objects.

 That would
> also remove any unnecessary temporary objects from statements making calculations very efficient (as stated earlier). For example:
> 
>     a = (b * c) + d;
> ->
>     a = b.opClone().opMulAssign(c).opAddAssign(d);
> 
> And as mentioned in some earlier posts, you could also have binary and unary clone operators. E.g. (I use '<-' here):
> 
>     a <- b;
> ->
>     a = b.opClone();

int a = 2;
int b = -3;
if (a<-b) { ... }
That particular one doesn't work. But := might be ok.


> The 'opClone()' function can also have a default implementation (done by the compiler). All the member variables are dublicated by default. For example:
> 
>     class Foo {
>         int a;
>         Bar b;
>     }
> 
> The default 'opClone' would then be (generated by the compiler):
> 
>     Foo opClone() {
>         Foo ret = new Foo;
>         ret.a = a;
>         ret.b = b.opClone();
>         return(ret);
>     }
> 
> If that's not wanted, or possible, then you can always implement the function by yourself, of course.
> 
> 
> Finally, there could also be easier way to create local objects (they too are discussed earlier in various posts). For example:
> 
>     Foo obj();  //a local object, already constructed

Currently, that compiles, and is equivalent to:

Foo function() obj;

October 25, 2006
On Wed, 25 Oct 2006 15:24:07 +0300, Don Clugston <dac@nospam.com.au> wrote:
> Kristian wrote:
[snip]
>> So, why don't add the 'opClone' operator to the language? I mean, there already are 'opXxx' functions. Why don't standardize the way of creating copies/clones?
>>   Then you could get rid of all the 'opXxx' and 'opXxx_r' functions: they can be expressed by using 'opClone' and 'opXxxAssign' only.
>
> Not necessarily. Consider Vector and Matrix.
>
> Matrix a;
> Matrix b;
> Vector c;
> a  = b * c;
> a = b.opClone().opMulAssign(c); // OK.
>
> But
> a = c * b;
> a = c.opClone().opMulAssign(b);
> // Oops -- we cloned the vector, should have cloned the matrix instead.
>
> In fact, in general you cannot even assume that
> (typeof(a*b) == typeof(a)) |  (typeof(a*b) == typeof(b))
>
> which means that cloning would sometimes create useless temporary objects.

Well, in you example (of course, I see your point though) 'Vector' should not have the 'opMulAssign(Matrix)' function (because it makes no sense), so "a = c * b" would then be read as:

    a = b.opClone().opMulAssign(c);  //ok


Lets have two classes, 'A' and 'B'.
'a' is type of 'A' and 'b' of 'B'.
Now if we have:

    typeof(a * b) == typeof(a)

    typeof(b * a) == typeof(b)

then 'mathematics will be broken' (or how should I put it). When this should be possible?

In the example above "c * b" should produce a Matrix, not a Vector.

*But* if it would be ok to produce Vector instead, then

    a = c.opClone().opMulAssign(b);

would also be correct (assuming that you can assign a 'Vector' in 'a'), and no useless temporary objects are created.


And if we have:

    typeof(a * b) == typeof(b * a) == typeof(a)

then only 'A' will contain the corresponding operators (i.e. 'opMulAssign(B)'). Thus there will be no problem to decide which object to clone.

So we can use cloning, and the 'opXxx' and 'opXxx_r' functions are not needed.
October 25, 2006
Kristian wrote:
> On Wed, 25 Oct 2006 15:24:07 +0300, Don Clugston <dac@nospam.com.au> wrote:
>> Kristian wrote:
> [snip]
>>> So, why don't add the 'opClone' operator to the language? I mean, there already are 'opXxx' functions. Why don't standardize the way of creating copies/clones?
>>>   Then you could get rid of all the 'opXxx' and 'opXxx_r' functions: they can be expressed by using 'opClone' and 'opXxxAssign' only.
>>
>> Not necessarily. Consider Vector and Matrix.
>>
>> Matrix a;
>> Matrix b;
>> Vector c;
>> a  = b * c;
>> a = b.opClone().opMulAssign(c); // OK.
>>
>> But
>> a = c * b;
>> a = c.opClone().opMulAssign(b);
>> // Oops -- we cloned the vector, should have cloned the matrix instead.
>>
>> In fact, in general you cannot even assume that
>> (typeof(a*b) == typeof(a)) |  (typeof(a*b) == typeof(b))
>>
>> which means that cloning would sometimes create useless temporary objects.
> 
> Well, in you example (of course, I see your point though) 'Vector' should not have the 'opMulAssign(Matrix)' function (because it makes no sense), so "a = c * b" would then be read as:
> 
>     a = b.opClone().opMulAssign(c);  //ok

But how does the compiler know that this is a legal transformation?

> Lets have two classes, 'A' and 'B'.
> 'a' is type of 'A' and 'b' of 'B'.
> Now if we have:
> 
>     typeof(a * b) == typeof(a)
> 
>     typeof(b * a) == typeof(b)
> 
> then 'mathematics will be broken' (or how should I put it). When this should be possible?

You've misunderstood me. My point was that it might be a third type. There's no guarantee that an opXXX function returns the same type as either of its operands.

One can define for example Vector * TransposeVector = Matrix.

opAdd() is a more general operation than opAddAssign(); it can't always be reduced to opClone() and opAddAssign().
October 25, 2006
On Wed, 25 Oct 2006 18:15:09 +0300, Don Clugston <dac@nospam.com.au> wrote:

> Kristian wrote:
>> On Wed, 25 Oct 2006 15:24:07 +0300, Don Clugston <dac@nospam.com.au> wrote:
>>> Kristian wrote:
>> [snip]
>>>> So, why don't add the 'opClone' operator to the language? I mean, there already are 'opXxx' functions. Why don't standardize the way of creating copies/clones?
>>>>   Then you could get rid of all the 'opXxx' and 'opXxx_r' functions: they can be expressed by using 'opClone' and 'opXxxAssign' only.
>>>
>>> Not necessarily. Consider Vector and Matrix.
>>>
>>> Matrix a;
>>> Matrix b;
>>> Vector c;
>>> a  = b * c;
>>> a = b.opClone().opMulAssign(c); // OK.
>>>
>>> But
>>> a = c * b;
>>> a = c.opClone().opMulAssign(b);
>>> // Oops -- we cloned the vector, should have cloned the matrix instead.
>>>
>>> In fact, in general you cannot even assume that
>>> (typeof(a*b) == typeof(a)) |  (typeof(a*b) == typeof(b))
>>>
>>> which means that cloning would sometimes create useless temporary objects.
>>  Well, in you example (of course, I see your point though) 'Vector' should not have the 'opMulAssign(Matrix)' function (because it makes no sense), so "a = c * b" would then be read as:
>>      a = b.opClone().opMulAssign(c);  //ok
>
> But how does the compiler know that this is a legal transformation?

1) 'Vector' has 'opMul(Matrix)':

    a = c * b;
->
    a = c.opMul(b);

Note that 'Vector.opMul(Matrix)' should not return 'Matrix'. If that would be the case, then the function will belong to 'Matrix', i.e. 'Matrix.opMulAssign(Vector)', and we could use cloning.
That is, there is no point in:

    Vector {
        Matrix opMul(Matrix m) {
            Matrix ret = new Matrix;
            ret = m.opClone();
            ret += this;
            return(ret);
        }
    }


2) 'Vector' has 'opMulAssign(Matrix)':

    a = c * b;
->
    a = c.opClone().opMulAssign(b);

We can use cloning obviously. But 'Vector.opMulAssign(Matrix)' returns 'Vector', so this case is not valid here (we have defined that "b * c" and "c * b" returns 'Matrix').


3) 'Matrix' has 'opMulAssign(Vector)':

    a = c * b;
->
    a = b.opClone().opMulAssign(c);

After the cases 1 and 2 are checked, this is a legal transformation.



>> Lets have two classes, 'A' and 'B'.
>> 'a' is type of 'A' and 'b' of 'B'.
>> Now if we have:
>>      typeof(a * b) == typeof(a)
>>      typeof(b * a) == typeof(b)
>>  then 'mathematics will be broken' (or how should I put it). When this should be possible?
>
> You've misunderstood me. My point was that it might be a third type. There's no guarantee that an opXXX function returns the same type as either of its operands.
>
> One can define for example Vector * TransposeVector = Matrix.
>
> opAdd() is a more general operation than opAddAssign(); it can't always be reduced to opClone() and opAddAssign().

Ah, I see. You're right of course.

Then that means that if a class has (or could have) a 'opXyzAssign' function, then there is no need to implement the 'opXyz' and 'opXyz_r' functions for it.

But when there is no corresponding 'opXxxAssign' function for 'opXxx'/'opXxx_r', then the compiler will use 'opXxx'/'opXxx_r' instead of 'opClone'. For example:

    A a;
    B b;
    C c;

    //typeof(b + c) == 'A'
    //typeof('A' * a) == 'A'
    a = (b + c) * a;
->
    a = b.opAdd(c).opMulAssign(a);

And:

    //typeof(b * b) == 'B'
    //typeof('B' + c) == 'A'
    a = b * b + c;
->
    a = b.opClone().opMulAssign(b).opAdd(c);

Two temporaties are always needed because 'typeof('B' + c)' is not 'B' or 'C' (i.e. there is no 'A.opAddAssign(B)' and 'B.opAddAssign(A)' functions).


In either case (there is 'opXxxAssign', or there can not be), there is no need to implement redundant functions, i.e. 'opXxxAssign' *and* 'opXxx'/'opXxx_r'.

Hence using 'opClone' reduces the number of needed operator functions by two thirds or one third!

(Usually there can be a 'opXxxAssign' function for an operator, so the number of needed operator functions will be reduced by two thirds normally!)


Now, if one tries to write an operator function 'A.opXyz(B)' that returns 'A' or 'B', the compiler will throw an error saying "define 'A.opXyzAssing(B)' or 'B.opXyzAssing(A)' function instead".
October 26, 2006
On Wed, 25 Oct 2006 20:01:50 +0300, Kristian <kjkilpi@gmail.com> wrote:
[snip]
> Now, if one tries to write an operator function 'A.opXyz(B)' that returns 'A' or 'B', the compiler will throw an error saying "define 'A.opXyzAssign(B)' or 'B.opXyzAssign(A)' function instead".

Of course, the error message is not wanted when you cannot access the source code of a class.
For example:

    class A;  //imported from a library

    class B {
        A opAdd(B);  //should be allowed
    }

Even if "A opAdd(B)" logically should be logically defined in 'A' (as "A opAddAssign(B)"), one cannot do it because the source code of 'A' is inaccessible.

One solution could be using a new keyword. For example:

    class B {
        extend A opAdd(B);
    }

Ok, 'extend' isn't probably a good one, I just quickly invented it because "A opAdd(B);" should be in 'A' so it kind of extends 'A', and "extend A opAdd(B);" can be read as "extend A" +  "opAdd(B);".


Ok, that would allow one to write:

    A a;
    B b;

    a = a + b;

But not:

    a += b;

So, the following could be possible instead:

    class B {
        extend A opAddAssign(B);
    }

Now it really extends 'A' because 'B' should/cannot have such an operator (it returns 'A' instead of 'B'). This would allow both the cases (with the help of 'opClone' as described earlier):

    a = a + b;
    a += b;

You can think of "A opAddAssign(B);" actually being defined in 'A' instead of 'B', whenever 'B' is used with 'A' that is.

(I am assuming here that 'B' can access all the necessary methods of 'A' to create such operators.)