Suggestion: wrapping up value type objects

Kristian kjkilpi at gmail.com
Wed Oct 25 10:01:50 PDT 2006


On Wed, 25 Oct 2006 18:15:09 +0300, Don Clugston <dac at nospam.com.au> wrote:

> Kristian wrote:
>> On Wed, 25 Oct 2006 15:24:07 +0300, Don Clugston <dac at 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".



More information about the Digitalmars-d mailing list