Interesting Research Paper on Constructors in OO Languages

Regan Heath regan at netmail.co.nz
Wed Jul 17 03:00:38 PDT 2013


On Tue, 16 Jul 2013 23:01:57 +0100, H. S. Teoh <hsteoh at 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
>> <cdillaba at 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/


More information about the Digitalmars-d mailing list