Composition over Inheritance in Pony
About a week ago I started poking around with the Pony language. My experience with this language thus far can be summed up like this — Pony seems to reside in that sweet spot between the ease and flexibility of Go and the strict safety constraints and performance of Rust. It has actors, classes, nominal typing via traits, generics, and structural typing via interfaces.
One objection many developers have when exposed to languages that have classes (or class-like behavior such as Rust and Go) is the lack of inheritance. After all, how can you possibly have classes without inheritance?
Even in languages that give me full inheritance capabilities, I am still a huge fan of composition over inheritance. I would rather contain or encapsulate the functionality that I need instead of inheriting it. I was pleased to see that this philosophy is embraced by Pony.
As an example to explore whether this really is a limitation, I decided to examine what it would look like to create a suite of actors that all share similar behavior, something that used to thoroughly annoy me when trying to accomplish in Akka. Traits and interfaces can both carry implementations, but they cannot carry state (fields). We often use inheritance to gain access to the protected members (e.g. state) of parents.
Let’s say I’m building a game and I have a Player actor that I want to be able to have inventory. This actor should have behaviors that respond to messages indicating an object moved into or out of its inventory. This common behavior can apply not just to players, but to shops, rooms, NPCs, etc.
I could use structural typing with an interface, but for my newbie example I want to be explicit that Players (or Shops, NPCs, Rooms, etc) can have inventory. To do this, I’ll create a trait called Container.
be enter_inv(ob: Any tag, from: Any tag)
be leave_inv(ob: Any tag, to: Any tag)
In this particular design, every entity within the game is an actor. As such, I’m perfectly fine with the Any type only being constricted to tags. (Tags are an opaque reference that is part of Pony’s reference capabilities system).
Now I can modify my player actor so that it is explicit about the fact that it can receive container-related messages:
actor Player is Container
Here’s where reliance on inheritance irritates people. How do you actually get the container state — the contents of the actor’s inventory? This is where we use encapsulation or composition, and give our Player actor an instance of a class that manages that kind of state for us:
actor Player is Container
let _container: StdContainer
new create(...) =>
_container = StdContainer
be enter_inv(ob: Any tag, from: Any tag) =>
// game object entered my inventory
_out.print("object entered my inventory")
be leave_inv(ob: Any tag, to: Any tag) =>
// game object left my inventory
_out.print("Object left my inventory")
What this gives me is the ability to treat any large swath of actors as though they can handle the entrance and exit of game objects into their inventory. I am free to create specializations (like writing code that rejects an object and puts it back where it came from). The implementation of StdContainer could emit events on a bus that are eventually gathered up to produce distributed, eventually consistent state so that other nodes in my game will be aware of this change to inventory.
I can then further add functionality to my Player actor that can also be shared (not inherited) by any number of other actors, like enabling the receipt of combat-related messages:
actor Player is (Container & Combatant)
let _resolver: CombatCalculator
be attacked_by(ob: Any tag) =>
I am still a complete Pony newbie and am just beginning my journey. However, so far I absolutely love what I’ve seen and I can’t wait to spend more time working with it, even if it doesn’t have inheritance 😀