December 11, 2006
Walter Bright wrote:
> Burton Radons wrote:
> 
>> Here's the difference:
>>
>>     struct S
>>     {
>>         this (int x)
>>         {
>>             w = calculate_something (x);
>>         }
>>
>>         this (int x, int y)
>>         {
>>             this (x);
>>             z = calculate (y);
>>         }
>>     }
> 
> 
> struct S
> {
>     static S opCall(int x)
>     {
>     S result;
>     result.w = calculate_something(x);
>     return result;
>     }
> 
>     static S opCall(int x, int y)
>     {
>     auto result = S(x);
>     result.z = calculate(y);
>     return result;
>     }
> }
> 
> It's 3 more lines of code. The two styles almost completely overlap, and since the latter is already in use, adding ctors just seems redundant.

OK here are the things that make me want constructors instead of static opCall:

It takes a lot longer to type.  You say only 3 more lines, but in that example 9 lines of constructor code becomes 12 lines of opCall code. The resulting code that is specific to these features is 25% fluff, and that includes the trivial curly braces.  Character wise it is worse.

The typing doesn't bug me as much as this though: what if the struct's name changes?  And what if the opCall is heavily overloaded when the name changes?  It's more unneeded code refactoring.  That is one reason D constructors are so cool, and it kinda sucks that structs don't have that too.

Also, static opCall is almost always used in the same way as a constructor.  I'd expect them to have the same syntax, but they don't. I think this, and some of the above reaons, result in the workaround perception - everyone expects the smooth 'this' syntax, but get static opCall instead.

Now I read that you would like to keep the semantic differences intact because they are useful.  Forcing the possibility of a bitwise copy of the struct before it is unleashed will apparently allow for cool stuff.  I don't think it's too unreasonable to have the 'this' identifier be a value rather than a reference in a struct constructor.  Thus you have a function that implicitly creates a blank instance of the struct, then allows the programmer to modify it via 'this', and implicitly returns the 'this' struct as static opCall would.
December 11, 2006
Chris Miller wrote:
> 
> char* p = new char[32];
> 
> Error: cannot implicitly convert expression (new char[](32)) of type char[] to char*
> 
> Should this be a special case? Currently it needs  (new char[32]).ptr

Well, the problem is that since this change, char[] is not the same thing as char*; IMO, this is a good thing.  You really should have to jump through an extra hoop to get there, because they're not the same. It's no different than a cast, if it is a little harder to type.

-- 
- EricAnderton at yahoo
December 11, 2006
Walter Bright wrote:
> More ABI changes, and implicit [] => * no longer allowed.
> 
> http://www.digitalmars.com/d/changelog.html
> 
> http://ftp.digitalmars.com/dmd.175.zip

Walter, this is a great update.  Especially the removal of the implicit cast for array pointers.

But I'd like to echo the other comments in this thread regarding structs.  IMO, we're not there yet.  I think folks are looking for a solution that does this:

- A ctor like syntax for creating a new struct
- No more forced copy of the entire struct on creation
- Something that is disambiguated from static opCall
- Ctors that are as clear to read as this() in classes and modules.

Again, thanks for the solid push to 1.0 this month.  I'm sure we haven't seen anything yet.

-- 
- EricAnderton at yahoo
December 11, 2006
Walter Bright wrote:
> Chris Miller wrote:
>> On Sat, 09 Dec 2006 13:59:24 -0500, Walter Bright <newshound@digitalmars.com> wrote:
>> 
>>> Chris Miller wrote:
>>>> Allow static opAssign to return an instance of the class that will be assigned to the lvalue:
>>> 
>>> This wasn't done because there are several use cases where you wouldn't want to erase all previous contents of the struct being assigned to.
>> 
>> But if it's optional it's up to the struct writer to use void as the return or not.
> 
> I don't think that'll work. He'll wind up being forced to set all the fields.

Doesn't follow - there might not be any to set other than those that are
an inherent part of the assignment operation.

For example, consider an immutable big integer class.  One might want

    Int x;
    ...
    x = 42;

as syntactic sugar for

    x = new Int(42);

Stewart.
December 11, 2006
Walter Bright wrote:
> Tom S wrote:
>> The Foo instance returned from static opCall is copied, thus the 'ctor hack' doesn't have real access to the object it's constructing, not to mention the overhead of copying the struct to another place on stack...
> 
> 
> It's time to put the recurring efficiency argument to bed. Consider this D code:
[...]

In many cases, it may be vary good. However consider the case where there is more than one return statement using different variables. You now need a much better optimizer to get this optimized down to "almost nothing". Furthermore, it doesn't reflect what is actually being done.

struct S
{
	static S err;
	int k, l;

	static S opCall(int i, int j)
	{
		S ret;
		ret.k=i;
		ret.l=j;

		if(ret.test)
			return ret;
		else
			ret err;
	}

	bool test(){...}
}


With constructors, it is not only simpler code, but looks like what is happening.

struct S
{
	static S err;
	int k, l;

	this(int i, int j)
	{
		k=i;
		l=j;

		if(!ret.test) this = err;
	}

	bool test(){...}
}
December 11, 2006
Brad Roberts wrote:
> 
> Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please?  And while you're in there.. how about destruction and RAII?

What ever happened to structs as aggregates?  I thought this was their entire purpose for being in D.  Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes?  In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago.  Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177.  That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.


Sean
December 11, 2006
Sean Kelly wrote:
> Brad Roberts wrote:
> 
>>
>> Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please?  And while you're in there.. how about destruction and RAII?
> 
> 
> What ever happened to structs as aggregates?  I thought this was their entire purpose for being in D.  Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes?  In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago.  Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177.  That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.

Classes are different from structs in two essential ways:

1. Polymorphism
2. Referential semantics

The two are actually interdependent, as you can't have polymorphism
comfortably unless you have reference semantics.

That's the important distinction. Other than that, it's good that they
share a number of valuable properties. Take private state for example.
structs as sheer unchecked aggregates would provide too little value to
be useful. Most of the time, aggregating some state together (date,
time, etc.) comes together with some desirable invariant that the aggregate should hold, meaning that not all possible bit patterns of the aggregate can be meaningful. So it's useful that struct has private state. Consequently, they should have member functions (setters, getters) and the such. They also should have constructors so they can put themselves in a meaningful initial state, and should have opAssign so they allow controlled overwriting of their state. (It's an accident that opAssign from the same type has not been implemented yet; it can be safely allowed.)

On the other hand, intercepting _duplication_ of structs naively would be a major breach in the object model, because it would frontally collide with (2) above: structs are values and the compiler can move and copy structs around discretionary using bitwise copying. That's why D
currently does not allow interception of copy construction and destruction. It might in the future, but in a way that does not clash with the object model. (That is doable.)

So my point was, as attractive the simple mantra "structs should be
aggregates" is, it turns out it's not that useful. So it's great that D
supports efficient and safe user-defined values.


Andrei
December 11, 2006
Andrei Alexandrescu (See Website For Email) wrote:
> Sean Kelly wrote:
>> Brad Roberts wrote:
>>
>>>
>>> Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please?  And while you're in there.. how about destruction and RAII?
>>
>>
>> What ever happened to structs as aggregates?  I thought this was their entire purpose for being in D.  Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes?  In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago.  Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177.  That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.
> 
> Classes are different from structs in two essential ways:
> 
> 1. Polymorphism
> 2. Referential semantics
> 
> The two are actually interdependent, as you can't have polymorphism
> comfortably unless you have reference semantics.
> 
> That's the important distinction. Other than that, it's good that they
> share a number of valuable properties. Take private state for example.
> structs as sheer unchecked aggregates would provide too little value to
> be useful. Most of the time, aggregating some state together (date,
> time, etc.) comes together with some desirable invariant that the aggregate should hold, meaning that not all possible bit patterns of the aggregate can be meaningful. So it's useful that struct has private state. Consequently, they should have member functions (setters, getters) and the such. They also should have constructors so they can put themselves in a meaningful initial state, and should have opAssign so they allow controlled overwriting of their state. (It's an accident that opAssign from the same type has not been implemented yet; it can be safely allowed.)
> 

"allow controlled overwriting of their state" -> Would that be for the case where the struct's private state (aka abstract state) is not just the bit pattern value of the struct, but also the contents of pointer/reference members?

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
December 11, 2006
Bruno Medeiros wrote:
> "allow controlled overwriting of their state" -> Would that be for the case where the struct's private state (aka abstract state) is not just the bit pattern value of the struct, but also the contents of pointer/reference members?

That too. The canonical example is simpler - reject invalid attempts at setting state. Consider:

struct BoundedInt(int min, int max) {
  ...
}

alias BoundedInt!(0, 100) Percent;

Percent soFar = 0;
...
soFar = bytesCopied * 100 / bytesToCopy;

Upon assignment, the bounded int structure should do a runtime check to make sure it is being set to a number within bounds.


Andrei
December 11, 2006
On Mon, 11 Dec 2006 10:50:41 -0800, Sean Kelly <sean@f4.ca> wrote:

> Brad Roberts wrote:
>>  Since the desired behavior is construction, let's stop kidding ourselves and actually give structs both the behavior and the syntax of construction, please?  And while you're in there.. how about destruction and RAII?
>
> What ever happened to structs as aggregates?  I thought this was their entire purpose for being in D.  Adding ctors makes sense because it merely simplifies initialization, but adding dtors, copy semantics, and opAssign all seem bent on making structs into something they're not. After all, isn't that why we have classes?  In fact, many of the instances where I was inclined to use structs simply to avoid the GC evaporated when stack construction of classes was added a few releases ago.  Aside from the addition of a ctor, I'm quite happy to leave structs exactly as they were before 177.  That said, I suppose I can appreciate the desire to give classes opAssign (even though it's somewhat weird), and perhaps the operator was added to structs simply for the sake of consistency.
>
>
> Sean


I used to be convinced that struct's needed constructors; but now I see that there is a steam-roll effect as the demand for one class-like attribute is proposed: suddenly people want more class-related functionality or people argue that there is a basis for additional features for the sake of consistancy.  Then the redesign of structs starts taking on the appearance of a reegineered, specialized class -- something of a "context switch" too since classes use reference semantics.  Perhaps we should be careful about hurtling too far in the direction of C++?

From the perspective of setting initial state, classes in the OOP context use constructors to do that.  Structs were never intended to have OOP like syntax (or so it seems to me).  Why can't structs just use an initializer like constants or statics?  I notice that Walter has added the struct initialization using S(x) syntax.  I think it is somewhat strange, but I suppose that was designed to be an alternative to constructor initialization?   What happened to something like a struct literal intializer (which only works for static structs and constant values)?  Why can't such initialization be extended to local structs?

...
struct S
{
   int i;
   bool b;
}

static S t = { 5, true };  // must be "static" or initialization won't work
...

The above only works for static Structs.  But that kind of initialization seems to be more consistant with the imperitive style of structs verses the OOP style of classes (if only non-statics could be initialized too, that is). When all is said and done, this is just like doing the same as S( 5, 2 ) since 0.177.

But I guess as soon as we start adding "methods" to the struct that are responsible for changing state in the struct, a whole new set of principles comes to work.  Adding constructor funtionality via "this()" is only one way to fix it.  opCall, the current way, is a rather ugly default.  If it is important to keep structs distinct from classes, then structs need to adopt a alternate way of doing initialization (but please not opCall).  I don't think "this()" really is the optimal way, though perhaps the most familiar due to the influence of classes.  I think there should remain a strong distinction between class and struct.

If, nevertheless, Walter decides to implement a "this" constructor for struct, I really hope he doesn't feel the need to go with a destructor and more class-like functionality as well.

Ironically, I feel more sympathetic to what appears to be Walter's opinion on the matter this time around. :D

-JJR