Go to the first, previous, next, last section, table of contents.


Fun With Messages

A "message", in the object oriented sense, is a function call in which Muq determines exactly which function to called by examining (typically) at runtime the first argument to the function. (This is in contrast to traditional function calls, in which the function to be applied is directly specified by the programmer.)

Each Muq object has an area devoted to holding functions implementing messages: The function for a hypothetical "sit" message would be stored in obj$m.sit, a "greet" function would be stored in obj$m.greet and so forth.

Let's suppose we are building a virtual world, and are creating objects which respond in various ways to a "sit" command. Here is an example of attaching a "sit" function to a "dog" object:

Stack:
makeIndex --> dog
Stack:
:: pop "wagwag?\n" , ; --> dog$m.sit
Stack:

Each muq object also has an obj$s.parents pointer indicating object(s) on which to search for functions, when a suitable one is not found on the object itself.

Message functions are invoked by "generic functions". To send a message "sit" to an object "dog", we apply the generic function "sit" to the object "dog":

Stack:
dog sit
wagwag?
Stack:

The generic function "sit" checks for a dog$m.sit function, and runs it if found. This is a handy way of making different objects to different things in response to the same command, without having to have one complicated function with all the different cases in it:

Stack:
makeIndex --> teenager
Stack:
:: pop "Fat chance!\n" , ; --> teenager$m.sit
Stack:
makeIndex --> soldier
Stack:
:: pop "Yessir! Right away, sir!\n" , ; --> soldier$m.sit
Stack:
makeIndex --> cat
Stack:
:: pop ; --> cat$m.sit
Stack:
dog sit
wagwag?
Stack:
teenager sit
Fat chance!
Stack:
soldier sit
Yessir! Right away, sir!
Stack:
cat sit
Stack:

If the generic function does not find a "sit" function on (say) "dog", it next checks the dog$s.parents object(s) for a "sit" function, and calls it if found. This lets a single object control the behavior of lots of other objects, which can be handy when one wants to easily change the behavior of all of them at once:

Stack:
makeIndex --> blind-mouse-1
Stack:
makeIndex --> blind-mouse-2
Stack:
makeIndex --> blind-mouse-3
Stack:
makeIndex --> mouse-parent
Stack:
mouse-parent --> blind-mouse-1$s.parents
Stack:
mouse-parent --> blind-mouse-2$s.parents
Stack:
mouse-parent --> blind-mouse-3$s.parents
Stack:
:: pop "Runrunrunrun!\n" , ; --> mouse-parent$m.sit
Stack:
blind-mouse-1 sit
Runrunrunrunrun!
Stack:
blind-mouse-2 sit
Runrunrunrunrun!
Stack:
:: pop "Chase farmer's wife!\n" , ; --> mouse-parent$m.sit
blind-mouse-1 sit
Chase farmer's wife!
Stack:
blind-mouse-3 sit
Chase farmer's wife!
Stack:

The advantage of this sort of arrangement is most evident when one is dealing with hundreds of objects of dozens of different kinds, and wishes to have objects of a given kind mostly behave the same, but occasionally have individual differences. We cannot do an example that big in this tutorial, but let's illustrate the principle by making one of the mice respond differently without changing the other two:

Stack:
:: pop "Squeak?\n" , ; --> blind-mouse-2$m.sit
Stack:
blind-mouse-1 sit
Chase farmer's wife!
Stack:
blind-mouse-2 sit
Squeak?
Stack:
blind-mouse-3 sit
Chase farmer's wife!
Stack:

If the generic function "sit" were to search both blind-mouse-1 and mouse-parent for a "sit" function without success, it would then search the mouse-parent$s.parents object, if any, and so forth, until it found a "sit" function or ran out of new objects to check. We won't give an example of this, but it means that mouse-parent could inherit functions from standard-animal, which might inherit functions from standard-movable-object, and so forth.

Hierarchies of this sort are a powerful way of building varied collections of objects which share behavior. When constructing a new kind of object, one can frequently inherit most of the required behavior from existing objects, and write code only for what is new and different about this particular object: Merlin's Magic Sword may inherit most of its behavior from a canonical-sword object, which may in turn inherit most of its behavior from a metal-tool object.

One may occasionally want a new object to inherit functions from more than one other object. (Be careful! The other objects might have contradictory requirements in some cases, or might someday be changed so that they do.) This may be done by storing a vector of objects in the parents slot, instead of a single object. Let's demonstrate this by building an auto that can drive, and a plane that can fly, and then creating a auto-plane that inherits both abilities:

Stack:
makeIndex --> auto
Stack:
:: pop "Vroom!\n" , ; --> auto$m.drive
Stack:
makeIndex --> plane
:: pop "Zoom!\n" , ; --> plane$m.fly
Stack:
makeIndex --> auto-plane
Stack:
[ auto plane | ]makeVector --> auto-plane$s.parents
Stack:
auto-plane drive
Vroom!
Stack:
auto-plane fly
Zoom!
Stack:

You may be wondering just where these generic functions are coming from, and where they wind up. They are so simple (two constants and two instructions) and so similar (only the name constant changes) that Muq simply creates them as needed: Any time you store a message function on an object, if there is not already a corresponding generic function, Muq simply creates it for you:

Stack:
makeIndex --> mouse
Stack:
run

**** Sorry: Undefined identifier: 'run'

Stack:
:: pop "Scurryscurryscurry!\n" , ; --> mouse$m.run
Stack:
run

**** Sorry: Needed object argument at top-of-stack[0]

Stack:
mouse run
Scurryscurryscurry!
Stack:
#'run
Stack: #<c-fn run>

The "run" symbol and function are stored in the current package, just like everything else you create.


Go to the first, previous, next, last section, table of contents.