Meet the constructors family

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.

Advertisement
Tagged

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: