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
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
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.