topnav
masthead

Series Info...#6: The Function Hierarchy

by Richard Bartle
November 14, 2001

Last time, I described the advantage of using commands as objects (in an object-oriented programming sense) in a MUD. Actually, this conclusion isn’t strictly true because functions are the true objects — commands are just examples of functions (I’ll explain how come when I get around to talking about parsers).

A property is a function of an object that returns the value of a single data item associated with that object. A very simple example might be a property that states player characters have a strength of 50. Rather than tie the code for this property to the PLAYER object (so player.strength() would return 50), I’m advocating that we tie it to the { STRENGTH PLAYER } object (so { strength player }.code() returns 50). { STRENGTH PLAYER } isn’t a command, it’s a property, and properties themselves are examples of functions.

This simple example shows another occasion for which we can utilize inheritance in a broader sense than is usually possible in MUDs. Suppose that a vanilla newbie player character is created with a whole set of attributes — STRENGTH, DEXTERITY, STAMINA etc.. If the default value for all of them is the same (say 50) then normally you’d have to define each one separately — player.strength()=50, player.dexterity()=50, player.stamina()=50 etc.. With the flexible system I’ve been describing, however, you just make STRENGTH, DEXTERITY, STAMINA and so on be instances of a class ATTRIBUTE. You can then define { attribute player }.code()=50 and that’s all you need.

Since we’re always going to be using the .code() method for our objects, we can drop it from the syntax — we know it’s always going to be there. We’ll replace it with a colon, to remind us. Thus, we could define { attribute player }: 50 to initialize all ATTRIBUTEs for players to be 50. If the default value for goblins were 30, we’d simply add the definition { attribute goblin }: 30.

Reservations

If we step back and look what we’ve done here, we’ve effectively divorced the inheritance hierarchy from the code. Although things like SWORD and STRENGTH are objects and classes in the hierarchy, they have no code attached — it’s all been moved over to the OOP objects, namely functions. Although that makes some kind of weird sense (if functions don’t have code, what in the world does?!) it nevertheless leaves the programmer with an uneasy sense of somehow being duped. We have classes, we have objects, we have inheritance, but it’s nothing to do with code. Code is attached to OOP objects that are functions, but they aren’t in a hierarchy and they don’t inherit from each other.

Well, actually they are in a hierarchy, only it’s an implicit one defined by the elements of the explicit hierarchy that make up the function’s parameters. They have inheritance, too, although in practice it turns out to be unnecessary. I’ll demonstrate shortly.

Atoms

First, though, to get away from tangling ourselves up with two separate notions of classes and objects, let’s call things like SWORD, GOBLIN and STRENGTH atoms. We thus have an explicit hierarchy of atoms, where child atoms have an "is a kind of" relationship with their parents. This is the atom hierarchy. Atoms have no executable code associated with them.

Functions are described by ordered lists of atoms, and they do have are pieces of executable code associated with them (e.g. evaluable expressions such as 50 and 6+6). I’m going to show you how, using the atom hierarchy, functions can form the OOP hierarchy we’ll be using.

Consider the following set of function/code pairs:

{ attribute creature }: 50

{ attribute player }: 60

{ dexterity creature}: 40

{ intelligence player }: 100

The atom hierarchy that goes with this is:

diagram

I’ve kept this as a simple tree, to make it easier to follow what’s going on — there’s no reason it couldn’t be a full hierarchy. ?? is the universal root.

Now, let’s try some function calls and figure out what they evaluate to. I’ll write these as [ atom1 atom2 ]() to show that they’re invocations and not definitions.

[ strength player ]() evaluates to 60

[ dexterity player ]() evaluates to 40 (NB: not 60. This is significant!)

[ intelligence player ]() evaluates to 100

[ strength mobile ]() evaluates to 50

[ dexterity mobile ]() evaluates to 40

[ intelligence mobile ]() evaluates to 50

[ strength intelligence]() is undefined

This exercise shows that the functions do have a partial ordering:

diagram

In theory, { dexterity player } should also have { attribute player } as a parent. I’ll explain why it doesn’t next time...

Recent Discussions on Notes from the Dawn of Time:

jump new