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.