-
Notifications
You must be signed in to change notification settings - Fork 26
RFC: Object Construction and Initialisation in Encore
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.
On new T(e1, ..., en)
, the following things happen, in this order:
- Memory for an instance i of
T
is allocated - All i's fields are initialised to default values
- Field initialisers for i's fields are run in the order in which they occur in the class
- The appropriate constructor in
T
is run with i asthis
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 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.
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)
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
.