Mixins, a flavor of multiple-inheritance, allow you to compose new objects from existing objects in ways that are hard or impossible with single-inheritance.
Back in the day, LambdaCore introduced the idea of a feature object, an object that was added to a player to give that player additional capabilities (command verbs). Its introduction and use was a nod toward the fact that sometimes an object is most easily described as a mixture of objects, rather than an instance or child of a single object.
The practice of using mixins in the construction of objects formalizes this pattern of composition.
Mixins are objects that are used to extend the functionality of another object, but that are not considered to be parent objects themselves. Stunt's implementation of multiple-inheritance is well suited to use of objects as mixins. Stunt's mixins can add both verbs and properties to child objects.
An object will typically have a parent object that is part of a traditional single-inheritance hierarchy. Mixins extend the functionality an object inherits from it parent. In the example below, $p
is the parent, and $a
, $b
and $c
mix-in additional functionality. The order of the parents is significant because the order defines how verbs are looked-up (resolved) when invoked on the child object.
create({$p, $a, $b, $c});
In the example above, $p
may be an object in the player hierarchy, the room hierarchy or some other traditional inheritance hierarchy in the database. Possible mixins include $attachable
, which adds attachments (external, file-based storage), and $loggable
, which standardizes the interface for logging. Player-specific mixins include $authenticatable
, which adds password based authentication.
Mixins can inherit from a common ancestor, or other objects in general, however since mixins are typically just bags of additional functionalty and state, inheritance isn't a requirement. I typically either extend $object
or $nothing
.
Consider creating a mixin when you have functionality you'd like to include in two disparate objects (like $player
and $room
) but you don't have a common ancestor on which it makes sense to define that functionality. With single inheritance you'd be forced to define the common functionality on $object
, whether or not it makes sense from a modeling perspective and whether or not you want every descendant of $object
to have that functionality. Instead, create a new mixin object that defines just the functionality in question, and mix it in to the list of parents for both $player
and $room.
Or, A Note About How Verbs and Properties are Found (Resolved)
When two parents or their ancestors define a verb with the same name, or provide a new default value for a common, inherited property, Stunt searches (resolves) references to that verb or property by searching each parent in turn, depth first up to the ultimate ancestor of that parent. In practice, this means that parents have an implicit priority, with the earlier parents (in the list of parents) having preference over parents later in the list.
There are restrictions on mixins (and Stunt-style multiple-inheritance, in general). These restrictions are extensions of the rules defined for single-inheritance in LambdaMOO.
First and foremost, proposed parents (or their ancestors) cannot define a property with the same name. They can share a property they both inherited from a common ancestor. This restriction extends to chparents()
as well, except that the rule applies to the object and its ancestors and its descendants. In short, there can be only one definition of a property in an inheritance graph.
Second, normal permission rules apply. If the player is not a wizard, then the player must own each of the proposed parents, or the proposed parent must have its f
flag set to true.
Mixins favor composition instead of inheritance as the means of reuse.
The benefit is the ease with which a new object can be composed from a collection of mixins, without having to resort to a convoluted inheritance graph, or redudant verbs and properties.