More Composition with Traits and State in Pony

Kevin Hoffman
2 min readJul 15, 2017

--

In a recent blog post, I showed some examples of how (and why) you can get around the so-called limitation of lack of inheritance in Pony. To illustrate this, I showed how we can create actors that all expose the same behavior by creating a trait to describe that behavior, like this one:

trait Container
be enter_inv(ob: Any tag, from: Any tag)
be leave_inv(ob: Any tag, to: Any tag)

This allowed us to create actors that implement a trait so that they can be treated the same, but we still had to do a little ugliness by implementing these behaviors:

be enter_inv(ob: Any tag, from: Any tag) =>
// game object entered my inventory
_out.print("object entered my inventory")
_container.add(ob)
be leave_inv(ob: Any tag, to: Any tag) =>
// game object left my inventory
_out.print("Object left my inventory")
_container.remove(ob)

This accomplished our goal, but we can do better. There is a trick we can employ here that will provide a default implementation for any actors that just want to implement inventory without any kind of customizations.

Let’s take a look at an updated version of the Container trait:

trait Container
be enter_inv(ob: Any tag, from: Any tag) =>
invstorage().add(ob)
be leave_inv(ob: Any tag, to: Any tag) =>
invstorage().remove(ob)
fun ref invstorage() : ActorStorage

There are a couple of interesting things here. First, note that there is now a default implementation associated with the first two behaviors. If you’ve used Scala traits or mixins in other languages, this concept should seem familiar to you.

Each of these implementations actually calls another method on the same trait, invstorage(). This returns an ActorStorage object. We could just as easily have made ActorStorage another trait, so that the underlying implementation of actor storage can change without altering the container trait.

Now our Player, NPC, Shop, et al, actors can all gain the default container implementation simply by exposing the means of inventory storage with a method like this:

fun ref invstorage(): ActorStorage => _container

This gives us a tremendous amount of power and flexibility. Rather than being constrained to work within a complex hierarchy of inherited classes, dealing with parents and siblings and artificial constraints, we can instead decide which groups of behaviors we want to slot in to our actors.

Combining this strategy of exposing a data class with traits, actors, Pony’s type safety and actor system gives developers an advantage that should not be overlooked.

--

--

Kevin Hoffman
Kevin Hoffman

Written by Kevin Hoffman

In relentless pursuit of elegant simplicity. Tinkerer, writer of tech, fantasy, and sci-fi. Converting napkin drawings into code for @CapitalOne

Responses (3)