August 04, 2006
On Thu, 03 Aug 2006 23:19:23 -0700, Kirk McDonald wrote:

> opDup appeals to me

Me too <g>

> , but more consistent would probably be a standard .dup property. Despite what others have said, := is not a terrible operator.

Agreed, but I'm not really fussed.

> However, having := be overloaded with a "dup" function instead of an "opDup" function is inconsistent with the other operator overloads.

Yes. Let's stick with consistency.

> So let's get the problem clear before we go proposing solutions: We want a standard way of getting a copy of an object.

Rather than using the term 'object' here, let's use the term 'item' because it is not so ambiguous. In this way, 'item' can mean every data type supported by D.

> We can either use an operator for this purpose (such as :=), or a standard method (such as .dup, as arrays currently use for the same purpose).
> 
> The := operator should work for classes, structs, arrays, and other primitive types. Arrays already have the .dup property, so := can use that. Remember that structs use value semantics. Assigning a struct the normal way copies its members. So, for structs and the various primitive types (that is to say, all of the types that use value semantics), := will be identical to a regular assignment.

Hmmm... not so certain about that. A shallow copy (bit-by-bit) is already performed by .dup and struct assignment, but we need a neat way to express that we want a deep copy done. That is, we want the information copied and not just the bits in the source item. Such that if an item contains references (dynamic arrays, objects, and other pointers) it might want to take copies of all their contents too.

  char[][] theFile;
  char[][] backup;
  . . .
  backup := theFile;  // Make a copy of all the strings, not just
                      // the references to the strings.

  class Foo
  {
      Bar b;
  }

  Foo a := aFoo;  // Takes a copy of the Foo object *and* the
                  // contained Bar object, not just a copy of
                  // the reference to the Bar object.


> I suggest a copy constructor as a way of overloading := for class objects. This is a C++ thing, but it is actually a fairly elegant solution, I think.

Whatever. I'm not fussed how its done so long as it makes writing and
reading code easier to do.

> The complication, now, is getting a constant way of getting a copy of some object or value when we don't actually want to assign it to anything (say, in a function call). My initial thought is to use unary : to mean "copy-of" (this is consistent with the := operator), but (not knowing much about how the parser works), I can't help but think that this might interfere with labels or the trinary ?: operator or something. Someone care to comment on that? The syntax looks fine:
> 
> fn( i, j, :obj); // send a copy of obj to the function

How about ...

    fn(i, j, auto := obj); //???

as the keyword 'auto' already implies a temporary object that is automatically destroyed when end of scope is reached.

> So, for both the proposed := copy-assignment operator and unary : copy operator, the various copying mechanisms would be:
> 
> class instances:                copy constructor

Ok, whatever.

> arrays/AAs:                     dup property

Nah ... needs a 'copy constructor' too, I think.

> structs,
Nah ... needs a 'copy constructor' too, I think.

> primitive types, etc:  by-value copy

Ok.

> We wouldn't even have to provide a copy constructor in Object, you know. Trying to use these operators on an instance of a class that doesn't define a copy constructor would just be a compiler error, like any other undefined operator overload or method you try to use.

Yeah, doesn't matter that much to me either way.

> Thoughts?

hmmm...chocolate-iced donut and hot coffee, but that's not relevant I guess. <g>

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
4/08/2006 4:29:20 PM
August 04, 2006
Kirk McDonald wrote:

[snip]

> fn( i, j, :obj); // send a copy of obj to the function
> 
> So, for both the proposed := copy-assignment operator and unary : copy operator, the various copying mechanisms would be:
> 
> class instances:                copy constructor
> arrays/AAs:                     dup property
> structs, primitive types, etc:  by-value copy
> 
> We wouldn't even have to provide a copy constructor in Object, you know. Trying to use these operators on an instance of a class that doesn't define a copy constructor would just be a compiler error, like any other undefined operator overload or method you try to use.
> 
> Thoughts?
> 

yeah :)

Sorry, Kirk, but this kinda stinks:

# fn( i, j, :obj);

I don't see any point of introducing obtuse operators when consistency would dictate something like this:

# fn( i, j, obj.dup);

The compiler can just as easily support a generic (shallow) .dup for classes as it can for other types. Alternatively, simply add a final dup() method to the root Object?

Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.








August 04, 2006
On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:

> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.

I'm not asking for a default deep copy. If you haven't defined one then its a compile time error. How would a function/method work with arrays as .dup only does a shallow copy. And how would it work in templates that use basic types? An operator overcomes these issues by letting the compiler know what the coder's intentions are and generate code accordingly. This could never work 100% with a "naming-convention" as it is no compiler enforced.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
4/08/2006 4:57:48 PM
August 04, 2006
Derek Parnell wrote:
> On Thu, 03 Aug 2006 23:19:23 -0700, Kirk McDonald wrote:
>>So let's get the problem clear before we go proposing solutions: We want a standard way of getting a copy of an object. 
> 
> 
> Rather than using the term 'object' here, let's use the term 'item' because
> it is not so ambiguous. In this way, 'item' can mean every data type
> supported by D.
> 
> 

"Item" it is!

>>We can either use an operator for this purpose (such as :=), or a standard method (such as .dup, as arrays currently use for the same purpose).
>>
>>The := operator should work for classes, structs, arrays, and other primitive types. Arrays already have the .dup property, so := can use that. Remember that structs use value semantics. Assigning a struct the normal way copies its members. So, for structs and the various primitive types (that is to say, all of the types that use value semantics), := will be identical to a regular assignment.
> 
> 
> Hmmm... not so certain about that. A shallow copy (bit-by-bit) is already
> performed by .dup and struct assignment, but we need a neat way to express
> that we want a deep copy done. That is, we want the information copied and
> not just the bits in the source item. Such that if an item contains
> references (dynamic arrays, objects, and other pointers) it might want to
> take copies of all their contents too. 
> 
>   char[][] theFile;
>   char[][] backup;
>   . . .
>   backup := theFile;  // Make a copy of all the strings, not just
>                       // the references to the strings.
> 
>   class Foo
>   {
>       Bar b;
>   }
> 
>   Foo a := aFoo;  // Takes a copy of the Foo object *and* the                   // contained Bar object, not just a copy of
>                   // the reference to the Bar object.
> 
> 

Do we want this to mean a deep copy? The only distinction we (I?) have been talking in terms of is the simple case, so I guess it hasn't been addressed, yet.

Python, for the record, allows classes to define both a __copy__ and a __deepcopy__ method (the double-underscores are Python's standard notation for operator overloading), and provides both a copy() and a deepcopy() library function. If a class doesn't provide a __copy__ or __deepcopy__ method, the library functions will use Python's introspective capabilities and do a naive copy or deep copy. (This is usually adequate.)

I am rapidly thinking that an operator is a bad idea. Please disregard my earlier musings on the idea. :-)

So! Define .dup to mean a shallow copy for classes. Object should not provide it. Classes that want to provide it, can. (This happily requires zero changes to the language! Hooray!) For consistency's sake, dup should be provided for primitive types. (This may simplify template code.) Structs should provide it as a bit-by-bit copy, just like regular assignment does now. I am split on whether users should be able to overload it for structs. I can't think of a good reason why one /would/ overload it, but then I can't think of a good, specific reason to disallow it, either.

A second standard property should be provided for deep copying. I suggest "deepcopy". As with 'dup', classes can provide this or not. Attempting to deepcopy an array of a class that doesn't provide a deepcopy property, or a struct containing such a class, should be a compile error. As with .dup, it should be provided for primitive types as an aid for template code.

If it is really wanted, := can be provided as a shortcut for assigning to a deepcopy. However (as I pointed out earlier) this would be inconsistent with the other operator overload names, and so I don't think it's really a good idea.

Using a copy constructor for either of these is probably not a good idea, as it doesn't imply anything about whether the copy is shallow or deep. (Naturally, classes can still provide one for their own nefarious purposes.)

-- 
Kirk McDonald
Pyd: Wrapping Python with D
http://dsource.org/projects/pyd/wiki
August 04, 2006
Derek Parnell wrote:
> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
> 
> 
>>Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
> 
> 
> I'm not asking for a default deep copy. If you haven't defined one then its
> a compile time error. How would a function/method work with arrays as .dup
> only does a shallow copy. And how would it work in templates that use basic
> types? An operator overcomes these issues by letting the compiler know what
> the coder's intentions are and generate code accordingly. 

True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.

On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?

You didn't say that, or rank deep-copy in any way, but one has to wonder?


All that aside, .dup() is still a better approach for shallow copy :)
August 04, 2006
kris wrote:
> Derek Parnell wrote:
>> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
>>
>>
>>> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
>>
>>
>> I'm not asking for a default deep copy. If you haven't defined one then its
>> a compile time error. How would a function/method work with arrays as .dup
>> only does a shallow copy. And how would it work in templates that use basic
>> types? An operator overcomes these issues by letting the compiler know what
>> the coder's intentions are and generate code accordingly. 
> 
> True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.
> 
> On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?
> 
> You didn't say that, or rank deep-copy in any way, but one has to wonder?
> 
> 
> All that aside, .dup() is still a better approach for shallow copy :)

With all that in mind - if .dup was given an op overload for classes and structs, couldn't that then be used to do the deep copy when it was imperative?

For example:

class C // built-in .dup copies i and the reference to str
{
    int i;
    char[] str;
}

class D // .dup is overloaded to do a default shallow and optional deep copy
{
    int i;
    char[] str;
    C opDup(bool deep = false)
    {
        C c = new C;
        c.i = this.i;
        if(deep)
            c.str = this.str.dup;
        else
            c.str = this.str;
        return c;
    }
}

- Dave
August 04, 2006
Dave wrote:
> kris wrote:
> 
>> Derek Parnell wrote:
>>
>>> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
>>>
>>>
>>>> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
>>>
>>>
>>>
>>> I'm not asking for a default deep copy. If you haven't defined one then its
>>> a compile time error. How would a function/method work with arrays as .dup
>>> only does a shallow copy. And how would it work in templates that use basic
>>> types? An operator overcomes these issues by letting the compiler know what
>>> the coder's intentions are and generate code accordingly. 
>>
>>
>> True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.
>>
>> On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?
>>
>> You didn't say that, or rank deep-copy in any way, but one has to wonder?
>>
>>
>> All that aside, .dup() is still a better approach for shallow copy :)
> 
> 
> With all that in mind - if .dup was given an op overload for classes and structs, couldn't that then be used to do the deep copy when it was imperative?
> 
> For example:
> 
> class C // built-in .dup copies i and the reference to str
> {
>     int i;
>     char[] str;
> }
> 
> class D // .dup is overloaded to do a default shallow and optional deep copy
> {
>     int i;
>     char[] str;
>     C opDup(bool deep = false)
>     {
>         C c = new C;
>         c.i = this.i;
>         if(deep)
>             c.str = this.str.dup;
>         else
>             c.str = this.str;
>         return c;
>     }
> }
> 
> - Dave


The issue I see, mixing deep & shallow copy like this, is that misuse would have to be noted at runtime rather than at compile-time?
August 04, 2006
kris wrote:
> Dave wrote:
>> kris wrote:
>>
>>> Derek Parnell wrote:
>>>
>>>> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
>>>>
>>>>
>>>>> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
>>>>
>>>>
>>>>
>>>> I'm not asking for a default deep copy. If you haven't defined one then its
>>>> a compile time error. How would a function/method work with arrays as .dup
>>>> only does a shallow copy. And how would it work in templates that use basic
>>>> types? An operator overcomes these issues by letting the compiler know what
>>>> the coder's intentions are and generate code accordingly. 
>>>
>>>
>>> True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.
>>>
>>> On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?
>>>
>>> You didn't say that, or rank deep-copy in any way, but one has to wonder?
>>>
>>>
>>> All that aside, .dup() is still a better approach for shallow copy :)
>>
>>
>> With all that in mind - if .dup was given an op overload for classes and structs, couldn't that then be used to do the deep copy when it was imperative?
>>
>> For example:
>>
>> class C // built-in .dup copies i and the reference to str
>> {
>>     int i;
>>     char[] str;
>> }
>>
>> class D // .dup is overloaded to do a default shallow and optional deep copy
>> {
>>     int i;
>>     char[] str;
>>     C opDup(bool deep = false)
>>     {
>>         C c = new C;
>>         c.i = this.i;
>>         if(deep)
>>             c.str = this.str.dup;
>>         else
>>             c.str = this.str;
>>         return c;
>>     }
>> }
>>
>> - Dave
> 
> 
> The issue I see, mixing deep & shallow copy like this, is that misuse would have to be noted at runtime rather than at compile-time?

Yea that's a drawback. Just trying to figure out how things could be done with '.dup' and I was thinking along the lines of deep copy not being used all that often (so it could just be built into an opDup overload and always made intentional). Or if the class developer always intended a deep copy for some reason then they could just code that as well.
August 04, 2006
Dave wrote:
> kris wrote:
> 
>> Dave wrote:
>>
>>> kris wrote:
>>>
>>>> Derek Parnell wrote:
>>>>
>>>>> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
>>>>>
>>>>>
>>>>>> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> I'm not asking for a default deep copy. If you haven't defined one then its
>>>>> a compile time error. How would a function/method work with arrays as .dup
>>>>> only does a shallow copy. And how would it work in templates that use basic
>>>>> types? An operator overcomes these issues by letting the compiler know what
>>>>> the coder's intentions are and generate code accordingly. 
>>>>
>>>>
>>>>
>>>> True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.
>>>>
>>>> On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?
>>>>
>>>> You didn't say that, or rank deep-copy in any way, but one has to wonder?
>>>>
>>>>
>>>> All that aside, .dup() is still a better approach for shallow copy :)
>>>
>>>
>>>
>>> With all that in mind - if .dup was given an op overload for classes and structs, couldn't that then be used to do the deep copy when it was imperative?
>>>
>>> For example:
>>>
>>> class C // built-in .dup copies i and the reference to str
>>> {
>>>     int i;
>>>     char[] str;
>>> }
>>>
>>> class D // .dup is overloaded to do a default shallow and optional deep copy
>>> {
>>>     int i;
>>>     char[] str;
>>>     C opDup(bool deep = false)
>>>     {
>>>         C c = new C;
>>>         c.i = this.i;
>>>         if(deep)
>>>             c.str = this.str.dup;
>>>         else
>>>             c.str = this.str;
>>>         return c;
>>>     }
>>> }
>>>
>>> - Dave
>>
>>
>>
>> The issue I see, mixing deep & shallow copy like this, is that misuse would have to be noted at runtime rather than at compile-time?
> 
> 
> Yea that's a drawback. Just trying to figure out how things could be done with '.dup' and I was thinking along the lines of deep copy not being used all that often (so it could just be built into an opDup overload and always made intentional). Or if the class developer always intended a deep copy for some reason then they could just code that as well.

Aye. But does it cover Derek's concern of deep-copy for arrays and so on? I suppose one argument could be that since deep-copy seems to be fairly rare, it's perhaps not really an issue to wrap the entity in an aggregate ;)

Seems like the two are sufficiently different in terms of both meaning and usage patterns; perhaps they should be isolated?

BTW: one concern I'd have with a root-Object dup() method is the need to cast the returned instance. Presumeably a more specific compiler implementation (of object.dup) would sidestep that need for casting?
August 04, 2006
kris wrote:
> Dave wrote:
>> kris wrote:
>>
>>> Dave wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> Derek Parnell wrote:
>>>>>
>>>>>> On Thu, 03 Aug 2006 23:47:47 -0700, kris wrote:
>>>>>>
>>>>>>
>>>>>>> Does deep-copy really need an operator? I have to wonder whether a simple naming-convention would do the trick instead  ... that way, you'd never have a /default/ deep-copy, and usage mistakes would produce a most welcome compile-time error.
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> I'm not asking for a default deep copy. If you haven't defined one then its
>>>>>> a compile time error. How would a function/method work with arrays as .dup
>>>>>> only does a shallow copy. And how would it work in templates that use basic
>>>>>> types? An operator overcomes these issues by letting the compiler know what
>>>>>> the coder's intentions are and generate code accordingly. 
>>>>>
>>>>>
>>>>>
>>>>> True. If I have an array of char*, an array of class refs, or something similar, and want a deep copy, then an operator might be a fair option. But so would the ability to add methods to arrays (for example), which would be far more powerful (and competitive with C#) than one specific deep-copy facility.
>>>>>
>>>>> On the other hand, one has to wonder how often deep copy is actually used? In 22 years of paid R&D (pretty much across the spectrum too) I can recall using deep-copy perhaps less than half a dozen times? As operations go, it's typically a rather expensive one. Most folk seem to try and find a way around that instead? Certainly it might be nice to have, but is it really more important than, say, fixing static-arrays?
>>>>>
>>>>> You didn't say that, or rank deep-copy in any way, but one has to wonder?
>>>>>
>>>>>
>>>>> All that aside, .dup() is still a better approach for shallow copy :)
>>>>
>>>>
>>>>
>>>> With all that in mind - if .dup was given an op overload for classes and structs, couldn't that then be used to do the deep copy when it was imperative?
>>>>
>>>> For example:
>>>>
>>>> class C // built-in .dup copies i and the reference to str
>>>> {
>>>>     int i;
>>>>     char[] str;
>>>> }
>>>>
>>>> class D // .dup is overloaded to do a default shallow and optional deep copy
>>>> {
>>>>     int i;
>>>>     char[] str;
>>>>     C opDup(bool deep = false)
>>>>     {
>>>>         C c = new C;
>>>>         c.i = this.i;
>>>>         if(deep)
>>>>             c.str = this.str.dup;
>>>>         else
>>>>             c.str = this.str;
>>>>         return c;
>>>>     }
>>>> }
>>>>
>>>> - Dave
>>>
>>>
>>>
>>> The issue I see, mixing deep & shallow copy like this, is that misuse would have to be noted at runtime rather than at compile-time?
>>
>>
>> Yea that's a drawback. Just trying to figure out how things could be done with '.dup' and I was thinking along the lines of deep copy not being used all that often (so it could just be built into an opDup overload and always made intentional). Or if the class developer always intended a deep copy for some reason then they could just code that as well.
> 
> Aye. But does it cover Derek's concern of deep-copy for arrays and so 

Nope <g>

> on? I suppose one argument could be that since deep-copy seems to be fairly rare, it's perhaps not really an issue to wrap the entity in an aggregate ;)
> 

I would say - everytime I've seen 'deep' copies done on the elements of an array of objects there's some sort of iterator and loop involved. But of course that's with C++ that doesn't have any notion of .dup built in.

> Seems like the two are sufficiently different in terms of both meaning and usage patterns; perhaps they should be isolated?
> 

I guess so, especially if you wanted to build something that would recurse through elements, like:

MyObject[] array;
...
array.dup;  // would .dup the array and call .dup on each element. Hmmm, how to copy just the array?
array.deep; // would .dup the array and call .deep on each element.

Something like that?

(Of course for arrays of value types the compiler would have to figure out that .dup and .deep do the same thing and don't recurse).

> BTW: one concern I'd have with a root-Object dup() method is the need to cast the returned instance. Presumeably a more specific compiler implementation (of object.dup) would sidestep that need for casting?

I agree.