You have a mutable data structure that you are building up over time in an actor and eventually need to send it to another actor. You could use the copying pattern. However, the copying pattern is not without issue.
The problem with the copying pattern is that you are… copying. Copying large data structures isn’t cheap. Even with small data structures, copying will result in many allocations and avoiding allocations is one of the critical pieces of advice in the Pony Performance Cheat Sheet.
If your use case meets one critical criterion, you can avoid copying. That criteria? You can “give away” the mutable data rather than having hold on to a reference so you can continue to update your copy later.
If that sounds like your problem, then welcome to your solution: “the isolated field.”
Let’s take a look at an example of the isolated field pattern. Pay particular attention to the
_data field on the
_data is the variable that we want to share between actors.
use "collections" actor Collector """ Receives characters via it's `collect` behavior and stores them. Once our collector receives 10 characters, it sends all 10 to the receiver. """ let _receiver: Receiver var _data: Array[U8] iso new create(receiver: Receiver) => _receiver = receiver _data = recover Array[U8] end be collect(char: U8) => _data.push(char) if _data.size() == 10 then let to_send = _data = recover Array[U8] end _receiver.receive(consume to_send) end actor Receiver """ Receives an array of characters from a collector and prints them as a string to standard out. """ let _out: OutStream new create(out: OutStream) => _out = out be receive(data: Array[U8] iso) => let s = String.from_array(consume data) _out.print(s)
The isolated field pattern combines a couple of features:
Let’s zoom in on the one key line from our example:
let to_send = _data = recover Array[U8] end
What’s going on with that? If you are new to Pony, that might be a very confusing bit of code. What you are looking at is a destructive read. What happens with the expression is evaluated? Well…
_datais rebound to a new empty
- the previous value of
_datais assigned to
Did you follow that? When the expression is done, we are left with 2
Array[U8] iso’s in scope:
- The local variable
to_sendwhich is an isolated array of 10 characters
- Our actor field
_datathat has been reinitialized to an empty array.
We are now free to take our mutable data that we’ve been collecting and send it along to the waiting