An important part of Pony’s capabilities is being able to say “I’m done with this thing.” We’ll cover two means of handling this situation: consuming a variable and destructive reads.
Consuming a variable
Sometimes, you want to move an object from one variable to another. In other words, you don’t want to make a new name for the object, exactly, you want to move the object from some existing name to a different one.
You can do this by using consume
. When you consume
a variable you take the value out of it, effectively leaving the variable empty. No code can read from that variable again until a new value is written to it. Consuming a local variable or a parameter allows you to make an alias with the same type, even if it’s an iso
or trn
. For example:
fun test(a: Wombat iso) =>
var b: Wombat iso = consume a // Allowed!
The compiler is happy with that because by consuming a
, you’ve said the value can’t be used again and the compiler will complain if you try to.
fun test(a: Wombat iso) =>
var b: Wombat iso = consume a // Allowed!
var c: Wombat tag = a // Not allowed!
Here’s an example of that. When you try to assign a
to c
, the compiler will complain.
Can I consume
a field? Definitely not! Consuming something means it is empty, that is, it has no value. There’s no way to be sure no other alias to the object will access that field. If we tried to access a field that was empty, we would crash. But there’s a way to do what you want to do: destructive read.
Destructive read
There’s another way to move a value from one name to another. Earlier, we talked about how assignment in Pony returns the old value of the left-hand side, rather than the new value. This is called destructive read, and we can use it to do what we want to do, even with fields.
class Aardvark
var buddy: Wombat iso
new create() =>
buddy = recover Wombat end
fun ref test(a: Wombat iso) =>
var b: Wombat iso = buddy = consume a // Allowed!
Here, we consume a
, assign it to the field buddy
, and assign the old value of buddy
to b
.
Why is it ok to destructively read fields when we can’t consume them? Because when we do a destructive read, we assign to the field so it always has a value. Unlike consume
, there’s no time when the field is empty. That means it’s safe and the compiler doesn’t complain.