Writing constructors by hand is so tedious that I am happy C# has object initializers. Well, sort of happy, because they are not really object initializers (they are just member assignments) and because of that they often collide with the configuration of the members (private
or readonly
). One thing I cannot deny — they offer really compact form of setting up the object:
new Point() { X = 5.3, Y = 7.2 };
Scala and Kotlin address the same problem providing also compact form, true initialization, however in not very pleasing manner:
class Point(var x: Double, var y: Double) { ... }
Without such constructor the structure of type definition is pretty clear, with constructor added all of the sudden part of the type is moved as parameters, so it looks like a method which is, and which is not at the same time. Different taste I assume.
Let’s write bare type Point
in Skila:
class Point var x = Double(13.2); var y Double; end
Some things worth noting — both fields are private by default, you cannot omit the typename unless you initialize the field (or property) with constructor, which is the case here. Type inference is useful feature to have but it is best served in moderate ratios.
Our Point
type has default constructor, but we cannot initialize Point
object with custom values. Let’s change it:
class Point in var x = Double(13.2); in var y Double; end
And that’s it — the keyword in
says ”add named parameter to constructor and initialize the given field with it”. In other words such constructor is added behind the scenes:
def init(x : Double = 13.2, y : Double) this.x = x; this.y = y; end
Because of the named parameters (note the colon) the compiler can add default values wherever it wants. Named parameters also mean user has to provide the names:
Point(x : 3, y : 15.5); Point(y : 7, x : 1); // the order does not matter Point(y : 1.5); Point(2, 3.5); // error, no names Point(x : 2); // error, missing `y` parameter
What can I say — I honestly like this design. It is as clear as C# code, but you don’t lose any benefit of real constructor. What’s more, you can use in
keyword as a parameter and an argument as well:
base class Animal in let weight Double; end class Human : Animal in let iq Double; let label String; def init(label String = "Joe", in) super(in); // calling base constructor this.label = label; end end // `label` is anonymous parameter Human("Jane",iq:130,weight:60);
The only requirement is in
parameter has to come last, it is small price to pay if you ask me.