The constructor
When building is more than copying fields — code that runs at birth.
Construction by named fields copies values, and that is all. When being born takes more — a derived value, a default that is not zero, a rule to enforce — the archetype declares a constructor.
archetype Potion {
name: str;
doses: int;
constructor(name: str) {
self.name := name;
self.doses := 3;
}
drink() {
if (self.doses = 0) {
Console.print(text: $"{self.name}: the flask is empty.");
return;
}
self.doses := self.doses - 1;
Console.print(text: $"{self.name}: one dose down, {self.doses} left.");
}
}
@entry(start)
object MyProgram {
start() {
var vigour := Potion(name: "vigour");
vigour.drink();
vigour.drink();
vigour.drink();
vigour.drink();
}
}
Code at birth
constructor(name: str) {
self.name := name;
self.doses := 3;
}
constructor is the only member of an archetype that carries a keyword. It runs the moment
Potion(name: "vigour") builds the instance: here it stores the name it was given and
decides that every potion is born with three doses — a default that is not the zero of
omitted fields.
Its parameters are construction’s new façade: the call gives what the constructor asks for, and the constructor decides what the fields become.
The constructor replaces construction by fields
As soon as an archetype has a constructor, everything is built through it. Try
Potion(name: "vigour", doses: 9):
error[T022]: 'Potion' has no parameter 'doses'
Fields are no longer parameters — only the constructor’s are. That is the point of the
mechanism: if Potion guarantees three doses at birth, nobody can build a nine-dose one
through the side door.
State keeps its rules
drink() refuses to go below zero: the empty flask says so, and nothing breaks. Constructor
and methods are the two ends of one discipline — one guarantees how an instance is born, the
others how it evolves.
Your turn
- Add a
doses: intparameter to the constructor and pass it toself.doses: the callPotion(name: "vigour", doses: 5)becomes legal again — because the constructor accepts it. - Have the potion born with
self.doses := 0;: the firstdrink()hits the empty flask. - Give the parameter a default —
constructor(name: str, doses: int := 3)— and get the suppleness of 5.4 back:Potion(name: "vigour")andPotion(name: "heal", doses: 5)both work.