Combining Capabilities

When a field of an object is read, its reference capability depends both on the reference capability of the field and the reference capability of the origin, that is, the object the field is being read from.

This is because all the guarantees that the origin reference capability makes have to be maintained for its fields as well.

Viewpoint adaptation

The process of combining origin and field capabilities is called viewpoint adaptation. That is, the origin has a viewpoint, and can "see" its fields only from that viewpoint.

Let's start with a table. This shows how fields of each capability "look" to origins of each capability.


iso field trn field ref field val field box field tag field
iso origin iso tag tag val tag tag
trn origin iso trn box val box tag
ref origin iso trn ref val box tag
val origin val val val val val tag
box origin tag box box val box tag
tag origin n/a n/a n/a n/a n/a n/a

For example, if you have a trn origin and you read a ref field, you get a box result:

class Foo
  var x: String ref

class Bar
  fun f() =>
    var y: Foo trn = get_foo_trn()
    var z: String box = y.x

Explaining why

That table will seem totally natural to you, eventually. But probably not yet. To help it seem natural, let's walk through each cell in the table and explain why it is the way it is.

Reading from an iso variable

Anything read through an iso origin has to maintain the isolation guarantee that the origin has. The key thing to remember is that the iso can be sent to another actor and it can also become any other reference capability. So when we read a field, we need to get a result that won't ever break the isolation guarantees that the origin makes, that is, read and write uniqueness.

An iso field makes the same guarantees as an iso origin, so that's fine to read. A val field is globally immutable, which means it's always ok to read it, no matter what the origin is (well, other than tag).

Everything else, though, can break our isolation guarantees. That's why other reference capabilities are seen as tag: it's the only type that is neither readable nor writable.

Reading from a trn variable

This is like iso, but with a weaker guarantee (write uniqueness as opposed to read and write uniqueness). That makes a big difference since now we can return something readable when we enforce our guarantees.

An iso field makes stronger guarantees than a trn origin, and a trn field makes the same guarantees, so they're fine to read. A val field is globally immutable, so that's fine too. A box field is readable, and we only guarantee write uniqueness, so that's fine too.

A ref field, though, would allow writing. So instead we return a box.

Reading from a ref variable

A ref origin doesn't modify its fields at all. This is because a ref origin doesn't make any guarantees that are incompatible with its fields.

Reading from a val variable

A val origin is deeply and globally immutable, so all of its fields are also val. The only exception is a tag field. Since we can't read from it, we also can't guarantee that nobody can write to it, so it stays tag.

Reading from a box variable

A box variable is locally immutable. This means it's possible that it may be mutated through some other variable (a trn or a ref), but it's also possible that our box variable is an alias of some val variable.

When we read a field, we need to return a reference capability that is compatible with the field but is also locally immutable.

An iso field is returned as a tag because no locally immutable reference capability can maintain its isolation guarantees. A val field is returned as a val because global immutability is a stronger guarantee than local immutability. A box field makes the same local immutability guarantee as its origin, so that's also fine.

For trn and ref we need to return a locally immutable reference capability that doesn't violate any guarantees the field makes. In both cases, we can return box.

Reading from a tag variable

This one is easy: tag variables are opaque! They can't be read from.

Writing to the field of an object

Like reading the field of an object, writing to a field depends on the reference capability of the object reference being stored and the reference capability of the origin object containing the field. The reference capability of the object being stored must not violate the guarantees made by the origin object's reference capability. For example, a val object reference can be stored in an iso origin. This is because the val reference capability guarantees that no alias to that object exists which could violate the guarantees that the iso capability makes.

Here's a simplified version of the table above that shows which reference capabilities can be stored in the field of an origin object.


iso object trn object ref object val object box object tag object
iso origin
trn origin
ref origin
val origin
box origin
tag origin

The bottom half of this chart is empty, since only origins with a mutable capability can have their fields modified.

results matching ""

    No results matching ""