Skip to content

Persistent Data Structures

Problem

You need to send mutable data from one actor to another while keeping a copy of it in your original actor.

Solution

use "collections/persistent"

actor Collector
  """
  Receives characters via it's `collect` behavior and stores them.
  Every 10 characters we receive results in the entire array being sent all to
  the receiver.
  """
  let _receiver: Receiver
  var _data: Vec[U8] = Vec[U8]

  new create(receiver: Receiver) =>
    _receiver = receiver

  be collect(char: U8) =>
    _data = _data.push(char)

  be send(to: Receiver) =>
    to.receive(_data)

actor Receiver
  """
  Receives an array of characters from a collector
  """
  be receive(data: Vec[U8]) =>
    // do something with `data`
    None

Discussion

That’s a pretty simple looking solution, especially when you compare it to the copying pattern, which is another way to solve this problem. So what’s going on here?

The key is that our _data vector isn’t mutable. In fact, var _data: Vec[U8] = Vec[U8] is creating a val. So, we aren’t dealing with mutable data; we are creating a new immutable vector each time we “mutate” it. That’s why we need to assign to _data in the collect method:

  be collect(char: U8) =>
    _data = _data.push(char)

And it’s also why we defined _data using a var rather than a let binding. Each time we update, we are creating a new vector and binding our _data variable to the new vector.

Because _data is a val, it’s already safe to share between actors. We don’t need to do anything special with it when we want to send it to another actor:

  be send(to: Receiver) =>
    to.receive(_data)

Persistent data structures go hand in hand with the copying pattern as a means of sharing mutable data between actors. Each method involves copying data. Which pattern should you pick? Whichever one will minimize the number of copies.

Persistent data structures involve copying on each update. The copying pattern involves a copy each time we send the data to another actor. As a general rule of thumb, figure out which you will do more: update or send. If you are updating more, use the copying pattern. If you are sending more, use persistent data structures.

In the end, that’s just a rule of thumb. Your best bet is to benchmark and pick the method that gives you the best performance for your use case.

If you aren’t familiar with persistent data structures, we suggest you pick up a copy of the book Purely Functional Data Structures. Another option is to download a copy of the thesis upon which the book is based. The Pony standard library contains a few, but you may need to design your own.

If you are interested in learning more about the persistent Vec and HashMap data structures from the Pony standard library, you can check out: