Skip to content

RFC: Object Construction and Initialisation in Encore

Tobias Wrigstad edited this page Feb 3, 2016 · 5 revisions

Instantiation: new

new T(e)

Comment: Python drops the new and uses T(e) -- this makes instantiation and function calls indistinguishable, which I don't like. I want a keyword, because I can grep for it/highlight it. Also, I don't want two keywords if I can get away with one.

Object Construction

On new T(e1, ..., en), the following things happen, in this order:

  1. Memory for an instance i of T is allocated
  2. All i's fields are initialised to default values
  3. Field initialisers for i's fields are run in the order in which they occur in the class
  4. The appropriate constructor in T is run with i as this

Constructors are optional.

Appropriate constructor means one that has a type signature matching the arguments.

Actors' constructors run asynchronously. All other constructors are executed synchronously by the calling "thread".

Since linearity allows type changes, it is possible to create a linear object, initialise it synchronously and then make it into an actor.

Constructors

Constructors are all called init. A class can have multiple init methods which must all have different type signature.

Constructors may have any return type. When evaluating new T(e), the constructor's return value is ignored.

Constructors can only be called once externally, right after the object has been created. Attempts at calling constructors "later" will be statically rejected. It is not possible to construct an object, send it a message, and then call its constructor.

Constructors may call other constructors in the same class. Constructors may call methods just like normal methods.

Getting the return values of constructors

The following code shows the creation of an object in two steps, first we create a new "pristine" instance, which executes the steps 1-3 of Object Construction above. Then we call the constructor explicitly. If T is an active object, d will be a Fut<V>, which we can block on until the object has been created.

let 
  n = new T               -- step 1-3
  d = n.init(e1, ..., en) -- step 4
in 
  ...

If this is believed too inconvenient, we might overload the new keyword depending on the called constructor's return type. If it is void, then new T(e) simply returns T, if it has type R different from void, then new T(e) returns a tuple (T, R). (This will require returning something from a constructor that a client should be able to assert have finished.)

A library function finish can be written using parametric polymorphism such that:

<T,R> def finish(a:T, f:Fut<R>) : T 
  let _ = f.get() in a

We should also consider assignments that unpack tuples, e.g.,

obj, future = new T(e)

Calling constructors is mandatory if one exists

In the case where init is not called implicitly by the new, the type system will prevent any dereference of the newly created object, except for calling init.