On Tue, 16 Jul 2013 23:01:57 +0100, H. S. Teoh <hst...@quickfur.ath.cx> wrote:
On Tue, Jul 16, 2013 at 06:17:48PM +0100, Regan Heath wrote:
On Tue, 16 Jul 2013 14:34:59 +0100, Craig Dillabaugh
<cdill...@cg.scs.careton.ca> wrote:

>On Tuesday, 16 July 2013 at 09:47:35 UTC, Regan Heath wrote:
>
>clip
>
>>
>>We have class invariants.. these define the things which must be
>>initialised to reach a valid state.  If we had compiler
>>recognisable properties as well, then we could have an
>>initialise construct like..
>>
>>class Foo
>>{
>>  string name;
>>  int age;
>>
>>  invariant
>>  {
>>    assert(name != null);
>>    assert(age > 0);
>>  }
>>
>>  property string Name...
>>  property int Age...
>>}
>>
>>void main()
>>{
>>  Foo f = new Foo() {
>>    Name = "test",    // calls property Name setter
>>    Age = 12          // calls property Age setter
>>  };
>>}

Maybe I'm missing something obvious, but isn't this essentially the same
thing as having named ctor parameters?

Yes, if we're comparing this to ctors with named parameters. I wasn't doing that however, I was asking this Q:

"Or, perhaps another way to ask a similar W is.. can the compiler statically verify that a create-set-call style object has been initialised, or rather that an attempt has at least been made to initialise all the required parts."

Emphasis on "create-set-call" :) The weakness to create-set-call style is the desire for a valid object as soon as an attempt can be made to use it. Which implies the need for some sort of enforcement of initialisation and as I mentioned in my first post the issue of preventing this intialisation being spread out, or intermingled with others and thus making the semantics of it harder to see.

My idea here attempted to solve those issues with create-set-call only.

[...]
The idea was to /use/ the code in the invariant to determine which
member fields should be set during the initialisation statement and
then statically verify that a call was made to some member function
to set them.  The actual values set aren't important, just that some
attempt has been made to set them.  That's about the limit of what I
think you could do statically, in the general case.
[...]

This still doesn't address the issue of ctor argument proliferation,
though

It wasn't supposed to :)  create-set-call ctors have no arguments.

if each level of the class hierarchy adds 1-2 additional
parameters, you still need to write tons of boilerplate in your derived
classes to percolate those additional parameters up the inheritance
tree.

In the create-set-call style additional required 'arguments' would appear as setter member functions whose underlying data member is verified in the invariant and would therefore be enforced by the syntax I detailed.

Now imagine if at some point you need to change some base class ctor
parameters. Now instead of making a single change to the base class, you
have to update every single derived class to make the same change to
every ctor, so that the new version of the parameter (or new parameter)
is properly percolated up the inheritance tree.

This is one reason why create-set-call might be desirable, no ctor arguments, no problem.

So, to take my idea a little further - WRT class inheritance. The compiler, for a derived class, would need to inspect the invariants of all classes involved (these are and-ed already), inspect the constructors of the derived classes (for calls to initialise members), and the initialisation block I described and verify statically that an attempt was made to initialise all the members which appear in all the invariants.

I think my approach of using builder structs with a parallel inheritance
tree is still better

It may be, it certainly looked quite neat but I haven't had a detailed look at it TBH. I think you've missunderstood my idea however, or rather, the issues it was intended to solve :) Perhaps my idea is too limiting for you? I could certainly understand that point of view.

I think another interesting idea is using the builder pattern with create-set-call objects.

For example, a builder template class could inspect the object for UDA's indicating a data member which is required during initialisation. It would contain a bool[] to flag each member as not/initialised and expose a setMember() method which would call the underlying object setMember() and return a reference to itself.

At some point, these setMember() method would want to return another template class which contained just a build() member. I'm not sure how/if this is possible in D.

R

--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Reply via email to