Nice TWiki > Dev > CurrentDiscussions > NiceConstructors > CustomConstructors (r1.26) TWiki webs:
Dev | Doc | Main | TWiki | Sandbox
Dev . { Changes | Index | Search | Go }
Construction is the process of creating a new object whose fields have some meaningful value. Currently, only the automaticly generated default constructor does that. It takes the value of each field as a named parameter.

Basically, a custom constructor could be defined as a normal method:

class Point { double x; double y; }
Point createPoint(double angle, double distance) = new Point(x: ..., y: ...);

The problem with this solution is that a subclass can not make use of the custom constructor for its parent, since it includes the creation of the object itself, instance of the parent class. Instead, we want the a custom constructors takes the new instance (this), and sets its fields. However, this cannot be done with a normal method, since we do not want to give a way to create an uninitialized instance, as this would be unsafe (like accessing this in a Java constructor is unsafe).

Therefore, we propose to add the concept of a custom constructor. Its declaration is similar to a method default implementation, except that it has no return type, and its name is the class C being constructed, preceded by new. The parameter list is arbitrary, as is the body, except the last instruction: it must be a call of the form new(...), targetting either the automatic constructor, or another custom constructor. This guarantees that all fields are set to a correct value. The body before this last instruction can be used to compute the arguments of this last call. this is not accessible anywhere in the custom constructor, since it is not properly initialized yet.

class Point { double x; double y; }
new Point(double angle, double distance) { this(x: ..., y: ...); }

This allows to create a new point with new Point(angle: ..., distance: ...). Furthermore, a subclass can be constructed by reusing this custom constructor:

class ColoredPoint extends Point { String color; }

var ColoredPoint redOrigin = new ColoredPoint(angle: 0, distance: 0, color: "red");

A custom constructor can also reuse the parent custom constructor indirectly, through the default constructor of the child class:

ColoredPoint(double angle, double distance, int color) { this(angle: angle, distance: distance, color: ...); }

-- DanielBonniot - 01 Oct 2003

Discussion

Calling new C(...) in the constructor seems redundant since C always matches the constructor name, and misleading since we might actually be constructing a subclass. Maybe a syntax like this would work better?

ColoredPoint(double angle, double distance, int color) { new(angle: angle, distance: distance, color: ...); }

Agreed, new C(...) is not a good syntax. We are thinking about using this(...) , but new(...) is an option too.

I updated the syntax above, using this(...). Which one do you prefer: this or new? DanielBonniot

I think this(...) would be too close to Java since the rules are different. (The constructor goes at the end, and this(...) suggests super(...) which doesn't make sense for Nice. But I'll defer to folks who are actually using the language. - BrianSlesinsky

I like being able to do things before calling the parent constructor, with the caveat that "this" is unavailable. But suppose you wanted to keep a hashmap of all objects of a certain type? In Java you'd add "this" to the hashmap in the constructor, which is unsafe but useful. Perhaps there should be a second pass that's started by automatically calling an init() method (if present) after all the constructors have finished?

The generated Java code would be something like:

  this(...);
  if(no subclass or subclass is not written in Nice) { this.init(); }

-- BrianSlesinsky - 13 Dec 2003

This is already possible using initializers, though the subclass check isn't implemented yet.

let Set<Foo> allFoos = new HashSet();

class Foo
{
  {
    allFoos.add(this);
  }  
  
}

-- ArjanB - 13 Dec 2003

Do you want to require that custom constructors should be defined within the class body? I think this is a good requirement because it distinguishes them more from methods. Also, they have access to "this" which should only be available from within the class body. Finally, constructors must be declared in the same package as the class anyway (package b cannot define a constructor for a.A).

There are no such restrictions: CCs can be defined outside classes, and in any package. On the other hand, they don't have access to this, as specified above (accessing this inside a constructor is unsound, since the instance is not yet fully constructed).

Okay, I misread "this is not accessible in the whole custom constructor" to mean "this is not accessible EVERYWHERE in the constructor" instead of "this is not accessible ANYWHERE in the custom constructor."

OK, my mistake, the grammar was imperfect :-) I restore the sentence in its new form, as I assume I got lost by accident.

How do custom constructors relate to SuperCall? It seems they are similar. I wonder if parent constructor invocation can be defined in terms of SuperCall? In Java they use super for both. Is there a reason not to use super in Nice too?

The idea is that every CC must (possibly indirectly) call a default constructor. This ensures that all fields are set. For every parent constructor (custom or default) there is a corresponding default constructor, which calls the parent and set the declared fields of the current class. So this is how you call super constructors. Since you don't call them directly, it would be illogical to use the super keyword.

Thanks, this explanation is what really helped me understand the proposal.

It is not clear from the examples if we are allowed to remove the labels for the constructor parameters, but I assume so since you can't use labels if you are subclassing a Java class anyway:

ColoredPoint(double angle, double distance, int color) { new(angle, distance, color); }

Do the same restrictions apply as in SuperCall? For example, is the following legal?

class A           { A(int f) { myF = f; } 
                    int myF;

                  }
class B extends A { B(int f) { new(myF: f / 2); } // passing different value for f
                                                  // is not allowed in SuperCall

Yes, there is no such restriction for CCs, this is legal.

The parameter list is arbitrary, as is the body, except the last instruction: it must be a call of the form new(...), targetting either the automatic constructor, or another custom constructor. This guarantees that all fields are set to a correct value.

I think this would be more clearly (and correctly) stated as:

The parameter list is arbitrary. If the class has a superclass, then the first instruction of the body must be a call of the form new(...) targetting either another constructor in the class being defined, or a constructor in the superclass of the class being defined. Any code may follow the new(...) call as in a method implementation.

No, we differ from Java here. We can indeed have instructions before the call to the other constructor. Those cannot access this of course, as it would be unsafe. In the current implementation, you cannot write anything after the call, although one could imagine an extension where it would be possible, provided any execution goes through calling another constructor once and only once. Currently, you would use an initializer if you need to do anything at the end.

Furthermore, require that the constructor must assign a value to every field that does not have a default value defined for it in the class's body; i.e. there are no implicit defaults (like in Java where an int defaults to zero if no value is assigned to it). Or, would you like to say that if a constructor parameter has the same name as a field, then that parameter's value is automatically assigned to the field?

This request is not explicitely needed, it's a consequence of the call to another constructor.

State that if a custom constructor is defined then the automatic constructor is not generated (true?).

At the moment the default constructor is still generated. It is still needed, as it is the one that really asssigns the fields. Later, we will introduce a way to make the default constructor private only.

Explain how self-calls dispatch in constructors, if different than dispatch from method bodies.

There is no self-call, since this is not available.

Explain the order in which initialization blocks and constructor bodies are run. Is it even necessary to have initialization blocks anymore?

-- BrianSmith - 18 Jan 2004

Initialization is performed after the execution of constructors. The chaining of constructors is explicit, so there is no special rule. -- DanielBonniot


How useful it is to have code execute before the constructor call? In Java and C++, I have only needed to do this a few times and in all such cases I was able to define simple functions that did the prior computations for me. How much expressivity would be lost if constructor bodies were limited to only a single constructor call, or a series of let expressions and a single constructor call?

Why do you want to restrict what can be done in CustomConstructors before the single constructor call? I see no reason for that and I'm sure someone find useful things you can't do with only let expressions.

It makes the concept simpler and easier to understand. The emperical evidence from Java suggests that making this simplification is not going to cause problems. Assume that custom constructors can only contain a single constructor invocation (no "let"). Then you can write every custom constructor as an equivalence:

    class Point { double x; double y; }
    Point(double angle, double distance)     = Point(x: angleOf(x,y), y: distanceOf(x,y));
or  new Point(double angle, double distance) = this(x: angleOf(x,y), y: distanceOf(x,y));
or  new Point(double angle, double distance) = new Point(x: angleOf(x,y), y: distanceOf(x,y));

You don't have to explain anything like whether or not self is available in constructor "bodies" and whether code is allowed before/after constructor invocations because constructors don't have bodies any more. It makes it clear that a custom constructor is just a an alternative for another (custom) constructor. It also emphasizes that constructors are for initializing a fields values, if you are planning to follow the _creation = construction + initialization definition Daniel gave in NiceConstructors._ -- BrianSmith - 20 Jan 2004

I often resent this limitation in Java. True you can always work-around it by using help methods, but I think it just clutters the code. Moreover, often you will be able to compute several values at once, but if you don't even have let, you will need to split the computation, once for each argument of the call.

It's true this would simplify the definition, but I'm afraid it is too restrictive. Besides, I expect that the question would pop-up from time to time: "why cannot I do computations before the call?", so we would need to add explanations anyway.

Fair enough. The reason I suggested the possibility of only allowing let was specifically to address the problem you mentioned (computing many values at once) while still having the syntax suggest that the constructor is only for field initialization. But then I realized that even the use of let would almost never be necessary. There is no way to enforce or even suggest (in the language syntax) that any code written in the constructor is relevant to field initialization, if constructs other than constructor calls are allowed in constructor bodies. Therefore, it doesn't seem to make much sense to forbid statements after the constructor call but allow them before. -- BrianSmith - 20 Jan 2004

The "new Point" syntax for defining constructors looks a lot like the syntax used for defining and instantiating anonymous classes (in Java anyway):

    class Point { double x; double y; }
    new Point(double angle, double distance) { ... }   // define Nice constructor
    new Point(a, d) { ...}                             // anon. class instantiation in Java
    new Point(angle:a,distance:d) { ... }              // presumably how anonymous classes
                                                       // would be instantiated in Nice.

When I see the keyword "new" I naturally think an instance is being constructed. Maybe the name of a constructor should just be the name of the class (without "new") like in Java, C#, C++?:

    class Point { double x; double y; }
    Point(double angle, double distance) { this(x: ..., y: ...); } // no "new"

Nice doesn't have anonymous classes but the syntax of CustomConstructors is a topic of discussion. Three choices are not decided definitely at the moment: new(...) or this(...) as constructor call in a CC, requiring "new" or not before the definition of a CC and where CC can be put: only inside class or outside too.

I think this(...) is not a good idea since it seems to imply that self-calls are allowed. Also, I don't like new(...) because I prefer to think of new as the allocate/construct/initialize operator. I think the Point(...) = Point(...) syntax I suggested above is clearest. -- BrianSmith - 20 Jan 2004

Yes, Point(...) has its merits. The only problem I see is that then you could be tempted to write such call with a different class, while that would not make sense. So it has redundant information.

At the moment I prefer this(...). I don't really see it as confusing with self-calls, which are this.foo(...). An advantage is that this(...) really has the same semantics as in Java: "call a constructor from the same class". So it makes sense to use the same syntax.

I agree that if you are going to allow code before/after the constructor call then this(...) makes the most sense out of any points considered to this point. -- BrianSmith - 20 Jan 2004

By the way, I think that this article is a good description of why self-call in constructors causes problems in Java and C#'s way of "fixing" the problem: http://www.jot.fm/issues/issue_2002_11/article4. -- BrianSmith - 19 Jan 2004

Interesting article but C# does only solve one of the problem Java has with constructors. As far as I understand C# doesn't solve the problem as shown in the Parent-Child example at 2/3rd of NiceConstructors page. -- ArjanB - 20 Jan 2004

Agreed, the semantics in C# is not satisfactory either. I think Brian agrees, and was saying exactly that. But the article seems slanted towards C# and does not make this point.

Sure, I know that C#'s solution is not perfectly safe. It disallows self calls in field initialization expressions but allows them in constructors. -- BrianSmith - 20 Jan 2004

What happens when you define a constructor that has a signature matching the default constructor?:

   class Point { double x; double y; }
   new Point(double x, double y)
     { this(x: ..., y: ...); }

Presumably, this would be helpful in order to add exception declarations, preconditions, postconditions, etc. to the default constructor. Yet, it is not clear how the fields of the class would be initialized because a this(...) call would be recursive.

The specification should state that "loops" in constructor calls are not allowed (e.g. a constructor constructor A is defined in terms of constructor B, while constructor B is defined (perhaps indirectly) in terms of constructor A, as in Java. And the Nice compiler needs to check for this, of course. -- BrianSmith - 20 Jan 2004

In case of a CC with the same signature as a default constructor, the current implementation just rejects the call to this(...) as ambiguous.

I agree loops should be detected. Amusingly, the current implementation fails at compile time with a StackOverflowError in such situation. So it seems that the check is there (by chance). OK, the error message could be improved :-)

DanielBonniot - 22 Jan 2004

When I wrote "What happens when..." I meant "what is designed to happen when...." In other words, is replacing the default constructor supposed to be allowed? If so, then how do the fields get initialized (using what syntax)? Using this(...) seems confusing in this situation because you are "calling" the constructor that you are replacing.

I did not really think about it before. A priori it seems to me that this situation could be forbidden. Would that be problematic? -- DanielBonniot

Consider:
    class X { int n; int m }
    new X(int n, int m)              // forbidden because it matches the 
        requires foo(n,m) {          // signature of the default implicit constructor
        this(n:n,m:m);               // confusing: looks (infinitively) recursive
    }       
    new X(Integer n, Integer m)
        requires foo(intValue(n), intValue(m)) {
        this(n:invValue(n), m:invValue(m));
    }
Why should the first constructor be rejected and the second one be accepted? I don't think the first one should be rejected on the grounds that the syntax required to support it is awkward.

It not just that the first one looks recursive, it is ambiguous, using the normal rules of the language. We might create an exception for this case (how would it be phrased precisely?), but before complexifying the language I would like to be convinced this is worth it.

I don't buy the argument about the second version. Integer is a completely different type than int, so there is no ambiguity. (OK, it happens that there is a special conversion in the bytecode, but I don't see how this changes the general point).

-- DanielBonniot - 30 Jan 2004

I put the second one there just to have something similar to compare the first one to. I assume that no matter what you decide the second one would be accepted. -- BrianSmith - 30 Jan 2004

OK. I now think it indeed makes sense to forbid the ambiguous situation. The idea of custom constructors is to offer alternative ways to construct an instance of a class. For instance, even though Points might happen to be implemented by a couple x,y, it is as valid to specify what instance you want with the couple distance,angle, so you can write a custom constructor with that signature. On the other hand, a custom constructor with signature x,y does not make sense, because such way to specify the instance already exists. If you wanted a different behaviour than the default constructor, it would be just confusing (like new Point(x,y) = this(x: y, y: x) !). And custom constructors are not the place to put initialization code, as discussed above, so that's not a reason to want a custom constructor either. -- DanielBonniot - 01 Feb 2004

Then how would one validate the initial values given for the fields of the instance? In the initializer? -- BrianSmith - 02 Feb 2004

When typing is not precise enough, you can specify the acceptable initial values in a requires clause. It would also be possible to use the initializer, but a contract seems more adapted, because it is part of the signature of the constructor (will be printed by NiceDoc, for instance). -- DanielBonniot

But, how would you add a requires clause to the default constructor? That was what motivated my example above. -- BrianSmith - 04 Feb 2004

I see. Since the default constructor corresponds to the internal representation of the class, the requirements could be put in the invariant of the class. -- DanielBonniot

It isn't always possible. Imagine that the precondition for the constructor is "an instance with the given (x,y) coordinates does not already exist in some pool." Then the initializer will add the new instance to the pool. In this case, the invariant for the class would be "an instance with the given (x,y) coordinates does exist in some pool." But, obviously the invariant can't be both at the same time. In other words, the constructor's precondition is only required to hold before construction, but not necessarily every point in time afterwards like the invariant. -- BrianSmith - 04 Feb 2004

Topic CustomConstructors . { Edit | Attach | Ref-By | Printable | Diffs | r1.27 | > | r1.26 | > | r1.25 | More }
Revision r1.26 - 04 Feb 2004 - 06:45 GMT - BrianSmith
Parents: WebHome > CurrentDiscussions > NiceConstructors
Copyright © 1999-2003 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback.

Dev.CustomConstructors moved from Dev.NonDefaultConstructors on 01 Oct 2003 - 21:08 by DanielBonniot - put it back