Aliasing means having more than one variable that points to the same object.
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 = 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 = 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 = a // Allowed!
What about aliasing other stuff? Everything else can be aliased as itself. So
ref can be aliased as
val can be aliased as
box can be aliased as
tag can be aliased as
What counts as making an alias?
There are two things that count as making an alias:
- When you assign a value to a variable. This could be a local variable or a field.
- When you pass a value as an argument to a method.
In both 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^ => consume a
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