A simple way to think of mixins is as “an interface with implementation”. If you have written Java interfaces, you have surely found yourself writing the same implementations over and over for each class that implements the interface. A mixin lets you write the implementation once and have it automatically included in each class that uses it.
Mixins work best when they are small, independent units of functionality, orthogonal to other classes and mixins. If your mixins obey this guideline, then the model of how mixins work is very simple: it is as if the body of each mixin were copied textually into each class that uses it.
One way to achieve that is to name each mixin property (attribute, method, etc.) with a prefix based on the mixin name. We hope to improve this situation by having the compiler warn when mixin methods collide without specifying that they are meant to be an override
If your mixins do interact, e.g., a mixin requires certain other methods or attributes, you can use the swf10 runtime to detect missing methods and attributes (the swf10 back-end will complain at compile time if there are methods or attributes that are referenced but not defined in a chain of mixins and superclasses). In other runtimes, you will not see the error until the method or attribute is actually referenced at runtime.
The simple model of how mixin inheritance works is that if you say:
<class name="sundae" extends="icecream" with="sauce,whippedcream,nuts">
methods are looked up first in the class (
sundae) then in each of the mixins (
nuts) and then in the super class chain (
icecreamand then in any superclasses of
icecream). The mixins are searched left-to-right, and then the superclass chain is searched. The key point is that mixins precede the superclass, and mixins are searched in the order they are listed. See test/mixins.lzx for a complete example of using mixins. This same search order is used when looking for methods, or attributes, or invoking super calls.
The exact model of how mixin inheritance works is given here. This is exactly how our compiler expands a mixin. Hence a mixin is really just a shorthand for reusing code in a class-like way. (The compiler is smart enough to share identical anonymous intermediate classes, so you don’t have to worry about efficiency.) If you need to understand something about how a mixin is working, this model should help you reason about it.
You can have a class with mixins extend another class that has other mixins, but caution: The compiler at present will not detect redundant mixins. The redundant mixin will appear to override the previous copy, which may lead to bizarre behavior.
This point is true even without mixins, but important to remember:
<handler>s do not override. When an event is sent to an instance (including the implicit events associated with attributes), every handler in the superclass chain, including handlers in any mixins, that handle that event will be invoked. The order of invocation is not defined. It is as if the event were broadcast to all possible handlers in any inherited mixin or superclass.
<method>s do override. When a method is called on an instance, the ‘most specific’ (i.e., first one found in the search order specified in 4) method is invoked. No other methods will be invoked, unless the most-specific one makes a super call (to the same method). It is as if a method were a point-to-point message, sent only to the nearest receiver. You need to be sure to choose carefully whether to use
<method>s when implementing your mixins, depending on your need for broadcast or point-to-point communication.
If you are a C++ programmer, you might worry about something akin ‘diamond inheritance’, say if you had multiple mixins with the same attribute or method name, but our mixins take the same approach as Dylan (and Python): all properties are virtual, so method signatures and attribute types need to be congruent, and the most-specific one specifies the value.
Mixins are a power tool. They give you a tremendous amount of flexibility and power, but they can easily be abused. Adhering strongly to the first two guidelines will save you a lot of grief. You should only resort to mixins that interact when there is no other approach.