Most of the time you will use init
constructor which works pretty much as the one seen in C#, Swift, or Python. The memory for object is already allocated, you just have to set the values for the object members.
Next we have field initializers — which you use, but you don’t define explicitly:
class Foo
let myField Int = 3;
end
That 3
will be moved to field initializer constructor, there is one per each class and it is called automatically just after alloc
constructor, which simply allocates memory.
Similarly to field initializer, there is a type initializer — this one can be written explicitly:
class Foo
static let myField Int = 3;
static let other Int;
static private def init()
this.other = 2;
end
end
In above code you see only one initialization inside type initializer, but compiler will drag down that 3
there as well.
All the fun is with the remaining two constructors — let’s start with new
one, its generated code looks always the same:
class Foo
•••
static def () Self // new constructor
let __this = Self.alloc();
__this.init();
return __this;
end
def init()
•••
end
end
There are some rules concerning new
constructor (it has to be static, it has to return Self/!Self
type, it cannot have name, it cannot be generic), but because its body is completely repetitive the compiler synthesizes it for every non-partial init
constructor there is.
For truly immutable types like Int
we can do better though — take a look at copy constructor:
static def (copy Self) Self
return copy;
end
Instead of init
constructor we defined new
constructor which returns the original object — this way we prevented creating new object (think: memory allocation) and for free we got new reference.
There is only one constructor left — companion constructor (anonymous factory). How many times when you wrote code in C# you were annoyed you couldn’t write such line ¹:
return new Tuple(3,4);
and you had to painfully instruct C# compiler:
return new Tuple<Int,Int>(3,4);
Skila has special syntax for creating tuple values, but faces the same problem — this is a constructor call and arity of the typename tells which type we are referencing. In the first snippet it would be a type Tuple
and the problem is there is no such type. Companion constructors fit just right in:
interface Tuple
new static def <T0,T1,out C>(_0 T0,_1 T1) Tuple<T0,T1,C>
where
C base T0;
C base T1;
return (_0,_1);
end
end
This particular example is an overkill in Skila, it serves only as an illustration. Note the differences, you can place companion constructor in any type, the result type does not have to be Self
, the method can be generic and you have to mark such constructor with modifier new
, otherwise compiler would assume it is indeed new
constructor. In other words you are marking anonymous factory method with new
modifier to be treated as new
constructor when called — yet, under the hood it is a regular method.
¹ Apparently I am not alone — see: Why can’t the C# constructor infer type? I am curious about history of resolving this issue in C# compiler, because targeting all possible types at the same time adds a lot of complexity.