Aliasing means having more than one reference to the same object, within the same actor. This can be the case for a variable or a field.
In most programming languages, aliasing is pretty simple. You just assign some variable to another variable, and there you go, you have an alias. The variable you assign to has the same type (or some supertype) as what’s being assigned to it, and everything is fine.
In Pony, that works for some reference capabilities, but not all.
Aliasing and deny guarantees
The reason for this is that the
iso reference capability denies other
iso variables that point to the same object. That is, you can only have one
iso variable pointing to any given object. The same goes for
fun test(a: Wombat iso) =>
var b: Wombat iso = a // Not allowed!
Here we have some function that gets passed an isolated Wombat. If we try to alias
a by assigning it to
b, we’ll be breaking reference capability guarantees so the compiler will stop us.
What can I alias an
iso as? Since an
iso says no other variable can be used by any actor to read from or write to that object, we can only create aliases to an
iso that can neither read nor write. Fortunately, we’ve got a reference capability that does exactly that:
tag. So we can do this and the compiler will be happy:
fun test(a: Wombat iso) =>
var b: Wombat tag = a // Allowed!
What about aliasing
trn? Since a
trn says no other variable can be used by any actor to write to that object, we need something that doesn’t allow writing but also doesn’t prevent our
trn variable from writing. Fortunately, we’ve got a reference capability that does that too:
box. So we can do this and the compiler will be happy:
fun test(a: Wombat trn) =>
var b: Wombat box = a // Allowed!
What about aliasing other stuff? For both
trn, the guarantees require that aliases must give up on some ability (reading and writing for
iso, writing for
trn). For the other capabilities (
tag), aliases allow for the same operations, so such a reference can just be aliased as itself.
What counts as making an alias?
There are three things that count as making an alias:
- When you assign a value to a variable or a field.
- When you pass a value as an argument to a method.
- When you call a method, an alias of the receiver of the call is created. It is accessible as
this within the method body.
In all three cases, you are making a new name for the object. This might be the name of a local variable, the name of a field, or the name of a parameter to a method.
In Pony, every expression has a type. So what’s the type of
consume a? It’s not the same type as
a, because it might not be possible to alias
a. Instead, it’s an ephemeral type. That is, it’s a type for a value that currently has no name (it might have a name through some other alias, but not the one we just consumed or destructively read).
To show a type is ephemeral, we put a
^ at the end. For example:
fun test(a: Wombat iso): Wombat iso^ =>
Here, our function takes an isolated Wombat as a parameter and returns an ephemeral isolated Wombat.
This is useful for dealing with
trn types, and for generic types, but it’s also important for constructors. A constructor always returns an ephemeral type, because it’s a new object.
For the same reason Pony has ephemeral types, it also has alias types. An alias type is a way of saying “whatever we can safely alias this thing as”. It’s only needed when dealing with generic types, which we’ll discuss later.
We indicate an alias type by putting a
! at the end. Here’s an example:
fun test(a: A) =>
var b: A! = a
Here, we’re using
A as a type variable, which we’ll cover later. So
A! means “an alias of whatever type