Consume and Destructive Read¶
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 second name for the object, 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 move it to a new location, most importantly for
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
c, the compiler will complain.
By default, a
consume expression returns a type with the capability of the variable that you are assigning to. You can see this in the example above, where we say that
Wombat iso, and as such the result of the
consume expression is
Wombat iso. We could also have said that
b is a
Wombat val, but we can instead give an explicit reference capability to the
fun test(a: AnIncrediblyLongTypeName iso) => var b = consume val a
The expression in line 2 of the example above is equivalent to saying
var b: AnIncrediblyLongTypeName val = consume a.
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.
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
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.