View mode: basic / threaded / horizontal-split · Log in · Help
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/11/12 9:52 AM, deadalnix wrote:
> Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
> Temporary object are used to store temporary state coming from some
> computation. In this case, the computation create the complexity, the
> object isn't created via default constructor.
>
> Or have you a use case in mind I don't think of ?

Here I meant a named temporary, such as e.g. a pivot in quicksort.

>> 3. Two-phase object destruction (releasing state and then deallocating
>> memory), which is useful, is made more difficult by default
>> constructors. Essentially the .init "pre-default-constructor" state
>> intervenes in all such cases and makes it more difficult for language
>> users to define and understand object states.
>>
>
> This one is made worse by the current state. You have to assume
> everywhere that your struct can be .init
>
> Even when it doesn't make any sense. RefCounted is a pathologic case of
> that.

One point I was making is that even with a default constructor, the 
definition and existence of .init pops all over the place, like a bubble 
in the carpet. I think it is a good engineering decision to simply make 
it the default state.

>> 4. Same as above applies to an object post a move operation. What state
>> is the object left after move? C++'s approach to this, forced by the
>> existence of default constructors and other historical artifacts, has a
>> conservative approach that I consider inferior to D's: the state of
>> moved-from object is decided by the library, there's often unnecessary
>> copying, and is essentially unspecified except that "it's valid" so the
>> moved-from object can continue to be used. This is in effect a back-door
>> introduction of a "no-resources-allocated" state for objects, which is
>> what default constructors so hard tried to avoid in the first place.
>>
>
> If we give struct a giveaway state (where the struct cannot be used
> unless it is reinitilized to a correct value) this problem disappear.

It doesn't disappear - it manifests itself in a different way 
(complexity in the language definition).

> Except in the case 5. (and heap allocated struct in general), that in
> fact seems to me the major issue.

I agree heaps with required allocated state are unpleasant to deal with 
in the current design.

My overall point, which is worth repeating, is not that these problems 
are insurmountable. Instead, we're looking at different choices with 
distinct tradeoffs. There's no solution with all pluses.


Andrei
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/11/12 9:56 AM, deadalnix wrote:
> Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
>> Could you please give a few examples? (Honest question.) Most structures
>> I define have an obvious quiescent state that vacuously satisfies the
>> invariant. Exceptions that come to mind are: (a) value types that must
>> always allocate something on the heap, see e.g. the contortions in
>> std.container; (b) values as permits (the existence of the value
>> guarantees a resource has been secured, as in scoped locks on mutexes).
>>
>
> invariant will explode at you face at runtime any time you use the
> struct wrong where a default constructor would have prevented such use
> in the first place.

I just mentioned that most of my structs have a natural 
invariant-abiding state.

> Worse, the faulty case can be created at any place where the struct is
> used and is likely to create a problem.
>
> In fact, such design rely on the well known « a good programmer don't do
> .... » which is known to be a very good way to design hard to use and
> error prone constructs.

There's a misunderstanding here.


Andrei
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 2012-10-11, 15:52, deadalnix wrote:

> Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
>>
>> We could (after all, C++ does it). There are a few disadvantages to
>> doing so, however.
>>
>> 1. Defining static data is more difficult. Currently, all static data is
>> statically-initialized. With default constructors, we'd need to define
>> the pre-construction state of such objects anyway, and then change the
>> compiler to call constructors prior to main(). I find the current design
>> simpler and easier to use.
>>
>
> CTFE is probably the answer here.

But not all functions are CTFE-able, so it's not a solution in all cases.

-- 
Simen
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
Le 11/10/2012 16:35, Andrei Alexandrescu a écrit :
> On 10/11/12 9:52 AM, deadalnix wrote:
>> Le 11/10/2012 14:19, Andrei Alexandrescu a écrit :
>> Temporary object are used to store temporary state coming from some
>> computation. In this case, the computation create the complexity, the
>> object isn't created via default constructor.
>>
>> Or have you a use case in mind I don't think of ?
>
> Here I meant a named temporary, such as e.g. a pivot in quicksort.
>

Well a pivot is not built out of nothing. So I don't see why any 
constructor have to be involved here.

However, postblit will be involved and can be of arbitrary complexity 
already.

>>> 3. Two-phase object destruction (releasing state and then deallocating
>>> memory), which is useful, is made more difficult by default
>>> constructors. Essentially the .init "pre-default-constructor" state
>>> intervenes in all such cases and makes it more difficult for language
>>> users to define and understand object states.
>>>
>>
>> This one is made worse by the current state. You have to assume
>> everywhere that your struct can be .init
>>
>> Even when it doesn't make any sense. RefCounted is a pathologic case of
>> that.
>
> One point I was making is that even with a default constructor, the
> definition and existence of .init pops all over the place, like a bubble
> in the carpet. I think it is a good engineering decision to simply make
> it the default state.
>

A giveaway state (as compile time state, not as runtime state) solve 
that problem nicely.

>>> 4. Same as above applies to an object post a move operation. What state
>>> is the object left after move? C++'s approach to this, forced by the
>>> existence of default constructors and other historical artifacts, has a
>>> conservative approach that I consider inferior to D's: the state of
>>> moved-from object is decided by the library, there's often unnecessary
>>> copying, and is essentially unspecified except that "it's valid" so the
>>> moved-from object can continue to be used. This is in effect a back-door
>>> introduction of a "no-resources-allocated" state for objects, which is
>>> what default constructors so hard tried to avoid in the first place.
>>>
>>
>> If we give struct a giveaway state (where the struct cannot be used
>> unless it is reinitilized to a correct value) this problem disappear.
>
> It doesn't disappear - it manifests itself in a different way
> (complexity in the language definition).
>

The complexity will have to be introduced anyway to handle @disable 
this() properly, so I don't really see that as an added complexity. It 
is more like getting the most of the complexity that is already planned 
to be added.

>> Except in the case 5. (and heap allocated struct in general), that in
>> fact seems to me the major issue.
>
> I agree heaps with required allocated state are unpleasant to deal with
> in the current design.
>
> My overall point, which is worth repeating, is not that these problems
> are insurmountable. Instead, we're looking at different choices with
> distinct tradeoffs. There's no solution with all pluses.
>

Well, why not let the programer make the choice ? Nothing have to change 
in regard to struct with no default constructor. And no complexity is 
added for existing stuffs.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On Thursday, October 11, 2012 08:19:23 Andrei Alexandrescu wrote:
> Could you please give a few examples? (Honest question.) Most structures
> I define have an obvious quiescent state that vacuously satisfies the
> invariant. Exceptions that come to mind are: (a) value types that must
> always allocate something on the heap, see e.g. the contortions in
> std.container; (b) values as permits (the existence of the value
> guarantees a resource has been secured, as in scoped locks on mutexes).

std.datetime.SysTime requires a valid TimeZone to function properly, but 
SysTime.init ends up with a null TimeZone, because it's a class, and you can't 
directly initialize a member variable with class object. The result of this is 
that SysTime can't have an invariant, because then SysTime.init would be 
invalid, and thanks to the fact that 
http://d.puremagic.com/issues/show_bug.cgi?id=5058 was resolved as invalid 
(the invariant gets called before opAssign even though I'd strongly argue that 
it shouldn't be), even assigning a valid value to a SysTime which was 
SysTime.init would blow up with an invariant. So, no invariant, even though it
really should have one.

Any situation where the init value is essentially invalid (like it would be 
with floating point types) makes it so that you can't have an invariant, and in 
many of those cases, having a default constructor which was always called 
would solve the problem. I'm still in favor of _not_ trying to add default
constructors given all of the issues involved, and I agree that on the whole,
init is a superior solution (even if it isn't perfect), but there _are_ cases
where you can't have an invariant because of it.

- Jonathan M Davis
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/11/2012 02:19 PM, Andrei Alexandrescu wrote:
> [snip good points.]
>

Those should be in the faq.

>
>> Really, there does not seem to me to be any point in having an invariant
>> for a struct, without a default constructor.
>
> Could you please give a few examples? (Honest question.) Most structures
> I define have an obvious quiescent state that vacuously satisfies the
> invariant. Exceptions that come to mind are: (a) value types that must
> always allocate something on the heap, see e.g. the contortions in
> std.container; (b) values as permits (the existence of the value
> guarantees a resource has been secured, as in scoped locks on mutexes).
>
>
> Andrei

I presume that many structures you define are templated and how obvious
their default state is often depends on the particular instantiation. A
large fraction of structs are local in idiomatic D code because of
local template instantiation.

Local structs do not have an obvious valid default state outside their
local context and there is no way to tell if it is valid in generic
code. Therefore, the issues you mentioned remain.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/11/2012 10:23 AM, Jonathan M Davis wrote:
> but there _are_ cases
> where you can't have an invariant because of it.

Except that you could write the invariant to be inclusive of the .init state.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On 10/10/2012 01:59 PM, Don Clugston wrote:
> On 10/10/12 13:27, Timon Gehr wrote:
>> On 10/10/2012 12:45 PM, Don Clugston wrote:
>>> ...
>>> Really, there does not seem to me to be any point in having an invariant
>>> for a struct, without a default constructor.
>>>
>>
>> One can use a dented invariant.
>>
>> struct S{
>>      bool valid = false;
>>      // ...
>>      invariant(){ if(valid) assert(...); }
>>      void establishInvariant()out{assert(valid);}body{...}
>> }
>
> Yes, you have to do something like that. It's absolute garbage. When you
> have a hack like that, I don't see the point of having invariants in the
> language.
> ...

Well, all invariants in Spec# follow this pattern. Every object has an
implicit boolean 'valid' field.
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On Thursday, October 11, 2012 13:06:34 Walter Bright wrote:
> On 10/11/2012 10:23 AM, Jonathan M Davis wrote:
> > but there _are_ cases
> > where you can't have an invariant because of it.
> 
> Except that you could write the invariant to be inclusive of the .init
> state.

Which would completely defeat the purpose of the invariant in many cases. The 
point is that it is invalid to use the init value. You can pass it around and 
set stuff to it and whatnot, but actually calling functions on it would be 
invalid, because its init state isn't valid. SysTime is a prime example of 
this, because it requires a valid TimeZone object, but its init value can't 
have one, because TimeZone is a class. So ideally, it would have an invariant 
which asserts that its TimeZone is non-null, but it can't have that, because 
opAssign unfortunately checks the invariant before it's called (which makes no 
sense to me - why would the state of the object prior to assignment matter? 
you're replacing it), so assigning a valid value to a default-initialized 
SysTime would fail the invariant.

- Jonathan M Davis
October 11, 2012
Re: What is the case against a struct post-blit default constructor?
On Thursday, 11 October 2012 at 20:59:32 UTC, Jonathan M Davis 
wrote:
> On Thursday, October 11, 2012 13:06:34 Walter Bright wrote:
>> On 10/11/2012 10:23 AM, Jonathan M Davis wrote:
>> > but there _are_ cases
>> > where you can't have an invariant because of it.
>> 
>> Except that you could write the invariant to be inclusive of 
>> the .init
>> state.
>
> Which would completely defeat the purpose of the invariant in 
> many cases. The
> point is that it is invalid to use the init value. You can pass 
> it around and
> set stuff to it and whatnot, but actually calling functions on 
> it would be
> invalid, because its init state isn't valid. SysTime is a prime 
> example of
> this, because it requires a valid TimeZone object, but its init 
> value can't
> have one, because TimeZone is a class. So ideally, it would 
> have an invariant
> which asserts that its TimeZone is non-null, but it can't have 
> that, because
> opAssign unfortunately checks the invariant before it's called 
> (which makes no
> sense to me - why would the state of the object prior to 
> assignment matter?
> you're replacing it), so assigning a valid value to a 
> default-initialized
> SysTime would fail the invariant.
>
> - Jonathan M Davis

This sounds more like a limitation of invariants, rather than a 
problem with .init. You make (imo) a valid point.

Would it be complicated for opAssign to first check memcmp(this, 
T.init), and only do entry invariant check if the comparison 
fails?

Potentially ditto on exit.
1 2 3 4 5
Top | Discussion index | About this forum | D home