February 13, 2009
Rainer Deyke wrote:
> Tom S wrote:
>> Rainer Deyke wrote:
>>> If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this.
>> No, they don't. Each instance of C has its own copy of the 't' reference.
> 
> That's like saying object slicing is an intentional feature.  Given the intended semantics of 'C', it's a bug.

It's not a bug.  There are differences between value types and reference types.  Just like how there are differences between atomic types and aggregate types.  Or constant types and mutable types.

If you want a distinct copy of something, then copy it.

  -- Daniel
February 13, 2009
Christopher Wright wrote:
> Rainer Deyke wrote:
>>> What mechanism for sharing state is available to by-reference objects but not by-value objects?
>>
>> struct C {
>>   T t;
>> }
>>
>> C c1;
>> C c2 = c1;
>>
>> If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this.  Moreover, the D language doesn't even give a hint as to whether T or a reference type or a value type.  If T is a template parameter, it could be both.
> 
> Okay, reference types don't have value semantics. That's exactly what I would expect.

That sounds tautological, but many newcomers to D are surprised by reference semantics when they expected value semantics.

> Why is it a problem?

Because reference types don't play well with RAII types.

Because unintentional object aliasing a problem orders of magnitude more common than unintentional object slicing, and at least an order of magnitude harder to detect and fix.

Because you can't correctly duplicate a multi-dimensional dynamic array in D by using the built-in 'dup' property.

Because having two syntactically similar ways of creating user-defined
types with different features sets and different semantics is confusing,
 especially if you later decide that you need a struct to be a class and
all your copies turn into reference or vice versa.

> Because there is no explicit builtin
> copying mechanism for classes? If there were such, would it be a problem
> if copying were not the default?

What exactly do you have in mind?
Would it allow classes to correctly work with RAII types?
Would it prevent unintentional object aliasing?
Would it allow multi-dimensional arrays to be correctly duplicated?
Would it remove the need for both classes and structs in the language,
or at least the need to switch between the two?


-- 
Rainer Deyke - rainerd@eldwood.com
February 13, 2009
Rainer Deyke wrote:
> Because unintentional object aliasing a problem orders of magnitude more
> common than unintentional object slicing, and at least an order of
> magnitude harder to detect and fix.
> 
> Because you can't correctly duplicate a multi-dimensional dynamic array
> in D by using the built-in 'dup' property.

Simply because shallow copies are the default. Usually you can't even have automatic deep copies. What if the array was supposed to be malloc'd or put in a contiguous memory block taken from a memory cache? A .dup that made a deep copy of it would introduce even a larger problem  (since it would seem correct) than remembering that dups/struct copies are shallow by default and you need special code to do otherwise.


> Because having two syntactically similar ways of creating user-defined
> types with different features sets and different semantics is confusing,
>  especially if you later decide that you need a struct to be a class and
> all your copies turn into reference or vice versa.
> [snip]
> Would it remove the need for both classes and structs in the language,

It's not a bug, it's a feature.

What if I decide that I want my int to be a float? Suddenly I have to add all those NaN checks, change my scanf calls, inspect all divisions and places where rounding might happen, not to mention any inline asm or binary shifts... Let's unify floats and ints so this cannot happen.


-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode
February 13, 2009
Daniel Keep wrote:
> It's not a bug.  There are differences between value types and reference types.  Just like how there are differences between atomic types and aggregate types.  Or constant types and mutable types.

This is a bug:

struct MathematicalVector {
  this(int size) {
    this.values = new T[size];
  }
  // No copy constructor.
  // Insert standard mathematical operators here.
  private T[] values; // Implementation detail.
}

I want MathematicalVector to be a value type (which is why I declared it as a struct).  However, it doesn't behave a value type because I forgot to write the copy constructor.  D doesn't cause the bug, but it certainly makes it easier to accidentally write this kind of bug.

By contrast, the compiler-generated copy constructors in C++ usually do the right thing for all but a handful of low-level resource-management classes.  Which is not to say that C++ doesn't have problems of its own - clearly it does, or I wouldn't be looking at D.


-- 
Rainer Deyke - rainerd@eldwood.com
February 13, 2009
Tom S wrote:
> Rainer Deyke wrote:
>> Because you can't correctly duplicate a multi-dimensional dynamic array in D by using the built-in 'dup' property.
> 
> Simply because shallow copies are the default. Usually you can't even
> have automatic deep copies. What if the array was supposed to be
> malloc'd or put in a contiguous memory block taken from a memory cache?
> A .dup that made a deep copy of it would introduce even a larger problem
>  (since it would seem correct) than remembering that dups/struct copies
> are shallow by default and you need special code to do otherwise.

C++ has a way of specifying how deep you want your copies to be at the declaration point:

std::vector<boost::shared_ptr<std::vector<int> > > v1, v2;
v2 = v1; // Shallow copy.
std::vector<std::vector<int> > v3, v4;
v4 = v3; // Deep copy.

Granted, C++ is a mess and you could do something similar in D by wrapping the reference in a value_semantics wrapper.

value_semantics!(value_semantics!(int[])[]) v5, v6;
v6 = v5; // Deep copy due to the value_semantics templated struct.

Unfortunately this doesn't solve the RAII problem: if value_semantics!(T) deletes the contained reference in its destructor, then it is no longer safe to embed a value_semantics!(T) variable in memory managed by the garbage collector.

>> Would it remove the need for both classes and structs in the language,
> 
> It's not a bug, it's a feature.

Having both value types and reference types in the language is (arguably) a feature.  Having no syntactic distinction between them at the point of use is at best a misfeature.  Forcing programmers to change their value types into reference types when they need polymorphism or inheritance, even though safe polymorphic value types are possible, is clearly a misfeature.

> [...] change my scanf calls [...]

Are you serious?


-- 
Rainer Deyke - rainerd@eldwood.com
February 13, 2009
Rainer Deyke wrote:
> Daniel Keep wrote:
>> It's not a bug.  There are differences between value types and reference
>> types.  Just like how there are differences between atomic types and
>> aggregate types.  Or constant types and mutable types.
> 
> This is a bug:
> 
> struct MathematicalVector {
>   this(int size) {
>     this.values = new T[size];
>   }
>   // No copy constructor.
>   // Insert standard mathematical operators here.
>   private T[] values; // Implementation detail.
> }
> 
> I want MathematicalVector to be a value type (which is why I declared it
> as a struct).  However, it doesn't behave a value type because I forgot
> to write the copy constructor.  D doesn't cause the bug, but it
> certainly makes it easier to accidentally write this kind of bug.
> 
> By contrast, the compiler-generated copy constructors in C++ usually do
> the right thing for all but a handful of low-level resource-management
> classes.  Which is not to say that C++ doesn't have problems of its own
> - clearly it does, or I wouldn't be looking at D.

C++ ctors won't do the right thing if you use pointers, which is the moral equivalent of using T[] inside MathematicalVector. If you refer to std::vector instead, then that's a carefully-defined type that does have the right copy constructor defined.

Andrei
February 13, 2009
Andrei Alexandrescu wrote:
> C++ ctors won't do the right thing if you use pointers, which is the moral equivalent of using T[] inside MathematicalVector. If you refer to std::vector instead, then that's a carefully-defined type that does have the right copy constructor defined.

So where's the D equivalent of std::vector?  Phobos has nothing.  Tango has containers, but they are also reference types.

I was under the impression that native arrays were intended to be used directly in D, something I rarely do in C++.  Certainly neither of the standard libraries has any hesitation about passing unadorned dynamic arrays around.


-- 
Rainer Deyke - rainerd@eldwood.com
February 13, 2009
Rainer Deyke wrote:
> Unfortunately this doesn't solve the RAII problem: if
> value_semantics!(T) deletes the contained reference in its destructor,
> then it is no longer safe to embed a value_semantics!(T) variable in
> memory managed by the garbage collector.

You can take additional steps to ensure safety with notifyRegister, if the type is an object. It's a hole in the language that you can't get notified about the destruction of anything but an object. Maybe there is some way to do this via the runtime and the collector rather than the variable.
February 13, 2009
Rainer Deyke wrote:
> Andrei Alexandrescu wrote:
>> C++ ctors won't do the right thing if you use pointers, which is the
>> moral equivalent of using T[] inside MathematicalVector. If you refer to
>> std::vector instead, then that's a carefully-defined type that does have
>> the right copy constructor defined.
> 
> So where's the D equivalent of std::vector?  Phobos has nothing.

It will.

> I was under the impression that native arrays were intended to be used
> directly in D, something I rarely do in C++.  Certainly neither of the
> standard libraries has any hesitation about passing unadorned dynamic
> arrays around.

Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL.


Andrei
February 13, 2009
On Fri, 13 Feb 2009 17:24:54 +0300, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Rainer Deyke wrote:
>> Andrei Alexandrescu wrote:
>>> C++ ctors won't do the right thing if you use pointers, which is the
>>> moral equivalent of using T[] inside MathematicalVector. If you refer to
>>> std::vector instead, then that's a carefully-defined type that does have
>>> the right copy constructor defined.
>>  So where's the D equivalent of std::vector?  Phobos has nothing.
>
> It will.
>
>> I was under the impression that native arrays were intended to be used
>> directly in D, something I rarely do in C++.  Certainly neither of the
>> standard libraries has any hesitation about passing unadorned dynamic
>> arrays around.
>
> Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL.
>
>
> Andrei

Will T[] get front, back, empty properties, and popFront(), popBack() methods?
Perhaps, its the time T[] internal structure is changed!
For example, ptr+size replaced by two pointers, some additional fields added etc..