Mantra allows you to dynamically add methods and entire delegate objects on a per-class basis at runtime. Naturally this facility is a double-edged sword. On the one hand, it allows you to add functionality to code for which you do not have the source. On the negative side, the potential for abuse here is high. To mitigate unintended consequences, you can only add functionality not override or alter functionality. With luck, this means that working programs will continue to work regardless of how a careless programmer meddles with methods dynamically. Currently, you cannot add fields to a class dynamically, but of course you can add a "getter" method.
Mixins
To make all instances of a particular class answer a new method, use the object.mixin() method. The first argument is the method name and the second argument must be a closure or function that takes a "self" object pointer as its first argument. For example, the following example adds toHex to every integer.
int.mixin("toHex",
{int self | return java {new mstring(Integer.toHexString(((mint)self).v))};}
);
println(32.toHex());
Delegation
Messages not understood by an object are forwarded to any delegates specified for the metaclass. For example, here is how to associate an error manager with every Vehicle object in the system.
class ErrorMgr {
error(string msg) { println("error: "+msg); }
}
ErrorMgr errors = ErrorMgr(); Vehicle.delegate(errors);
You can now send an error message to any Vehicle instance and it will be forwarded to the error manager.
Car c = Car(); println("Car type: "+c.class);
c.error("we crashed");
emits:
Car type: <Car>
error: we crashed
The difference between a delicate and a mixin is that the mixin gets a self pointer and so it knows how to access the original target of the message send. Delegates are simply forwarded the message, but, on the bright side, you can attach any object as a delegate.
> The difference between a delicate
Should be "delegate"