I’ve been encountering some programming situations lately that cried out for some aspect-oriented features that weren’t in Slag – until tonight!
A bit of background: aspect-oriented programming (AOP), in a nutshell, is being able to write your program as independent layers of functionality that are merged together to create the final result. For example, mixing drawing code in with your game logic is frowned upon, since if you need to entirely re-do either of those aspects – or port your software to a different system – then you’ve got to wade through everything looking for the bits and pieces you need to change.
This Slag code can be tough to maintain:
class Spaceship
METHODS
method init: set_position; load_graphics
method update: ...
method draw: ...
endClass
class Laser
METHODS
method init: set_position; load_graphics
method update: ...
method draw: ...
endClass
while this AO Slag code behaves the same and is easier to work with in the long run:
class Spaceship
METHODS
method init: set_position
method update: ...
endClass
class Laser
METHODS
method init: set_position
method update: ...
endClass
...
overlaying augment Spaceship
METHODS
method init: underlying; load_graphics
method draw: ...
endAugment
overlaying augment Laser
METHODS
method init: underlying; load_graphics
method draw: ...
endAugment
* * *
In retrospect I realize that Slag’s AO features were focused on creating static class definitions and what it needs are some AO features to assist dynamic calls between objects.
Prime example: Plasmacore v2 operates on a system of view windows, each with its own 2D transformation matrix (usually there’s just one window, btw). For a view to be drawn it needs to push its transformation matrix onto the transform stack, draw itself, and then pop off its transformation matrix.
To allow the draw() method to be easily overridden I can’t put the transform adjustments in that method itself, so what I end up with is:
view.push_transform view.draw view.pop_transform
It’d be really nice to allow an application programmer to just call “view.draw” and have the transforms automatically pushed and popped. There’s various kludgy ways to do it – especially renaming the actual draw to something else – but I came up with a more elegant way that was fairly quick to implement.
Slag v2 now has wrapper methods. If you call a method “fn” and there’s a method “fn-wrapper” defined, fn-wrapper() is the method that ACTUALLY gets called, and it should call the original fn() at some point. The View class would now look like this:
class View
METHODS
method draw: ...
method draw-wrapper:
push_transform
draw
pop_transform
From the outside you’d just call “view.draw” and it would actually call “view.draw-wrapper”.
The reason why you couldn’t do this with an ordinary aspect is that the calls to push/pop transform would become embedded in the actual draw code, which would make overriding draw() a hassle.
All of that is almost true. There’s an additional twist – Murphy noticed a flaw in this new system and suggested a very elegant solution!
The problem is, say that class Beta extends Alpha and Beta defines draw-wrapper but Alpha doesn’t. In accordance with the standard rules of polymorphism, if you were to write:
local Alpha a = Beta() a.draw
then it would call Beta::draw() without calling Beta::draw-wrapper() since the call context is class Alpha and the compiler can’t know for sure that the object will have the wrapper method.
The solution is to take any class with both “fn” and “fn-wrapper” defined and, in the compiler, change the names to be “fn-inner” and “fn”, respectively. The name is unchanged in base classes without wrapper methods, and the wrapper methods take on the original name in extended classes so that they’re called polymorphically.
I added wrapper methods into the compiler tonight – only took me about an hour after getting the idea ironed out!