Muq is more object-oriented than (say) Java: In Muq everything, including for example integers, really is treated consistently as an object. (2)
But in this section we are concerned specifically with user-defined classes, inheritance relationships between those classes, and the creation and use of instances of those classes.
In the Muq Object System, fields are called "slots". (3)
Here are the bare essentials of creating and using an object.
root: defclass: Geek :slot :name :slot :language ; root: 'Geek makeInstance root: #<a Geek 4e41861b> --> _geek1 root: "Linus" --> _geek1.name root: "C" --> _geek1.language root: _geek1 ls :name "Linus" :language "C" root: _geek1.name root: "Linus"
Slots may have default initial values defined:
root: defclass: Geek :slot :name :initval "Kim" :slot :language :initval "C" ; root: 'Geek makeInstance --> _geek1 root: _geek1 ls :name "Kim" :language "C" root:
Sometimes you want the initial value of a slot to be computed at object creation time, rather than be a fixed constant. A typical situation is when you wish each object to possess one or more index subobjects, and you don't want them shared between all instances of the class.
Use the :initform
keyword with an anonymous function in
such situations:
root: defclass: geek :slot :name :initval "Kim" :slot :likes :initform :: makeIndex ; :slot :dislikes :initform :: makeIndex ; ; root: 'Geek makeInstance --> _geek1 root: _geek1 ls :name "Kim" :dislikes #<Index _ 2f41db15> :likes #<Index _ 2f49da15> root: t --> _geek1.dislikes["Smurfs"] root: t --> _geek1.dislikes["Jocks"] root: _geek1.dislikes ls "Jocks" t "Smurfs" t root: t --> _geek1.likes["Filk"] root: t --> _geek1.likes["Babylon5"] root: _geek1.likes ls "Babylon5" t "Filk" t root:
By default, object slots are readable and writable by the
owner, and readable by others who obtain a pointer to the
object. You may change this using the :prot
keyword.
For example, suppose you want to create a class to contain PGP keypairs, with the public key to be readable by anyone but the private key readable only by the owner:
root: defclass: PgpKey :slot :publicKey :prot "rwr-r-" :slot :privateKey :prot "rw----" ; root:
(The Muq server actually provides special types for Muqnet private keys which are unreadable even by the owner -- a production implementation might use them.)
Use the :isA
keyword to define subclasses of a
previously defined class:
root: defclass: Flier :slot :wings :initval 2 ; root: defclass: Animal :slot :warmBlooded :initval t ; root: defclass: Bird :isA 'Flier :isA 'Animal ; root: 'Bird makeInstance --> _myBird root: _myBird ls :warmBlooded t :wings 2 root:
Here Flier
and Animal
are base classes, and
Bird
is a subclass of both Flier
and
Animal
, and hence has both a slot giving how
many wings it has, and a slot recording whether it is
warmblooded.
Sometimes you want a slot to be shared between all instances
of a class. Perhaps you have lots of Vassals in your
simulation, but they must always all have the same Queen.
Use the :allocation :class
slot modifier
in such cases:
root: defclass: Vassal :slot :queen :allocation :class :initval "Victoria" ; root: 'Vassal makeInstance --> _toby root: 'Vassal makeInstance --> _rupert root: _toby ls :queen "Victoria" root: _rupert ls :queen "Victoria" root: "Elizabeth" --> _toby.queen root: _toby ls :queen "Elizabeth" root: _rupert ls :queen "Elizabeth" root:
Message-passing is central to any object-oriented programming system: It lets a single abstract operation have different detailed implementations depending on the class of the object(s) at hand, and allows those implementations to be cleanly separated from each other rather than tangled together in some central maintainance headache of a function.
"Generic functions" provide Muq with message-passing.
Unlike languages like Java and C++ in which message-passing is done in a fashion inconsistent with the vanilla function facilities of the language, Muq generic functions are fully consistent with the vanilla function facilities of the language: Generic functions are invoked exactly as are normal functions (the caller need not even know whether they are generic or vanilla), may be passed around and stored in slots like vanilla functions, and in general may be used anywhere a vanilla function may be used, in exactly the same way a vanilla function would be used. In fact, in terms of superficial implementation, generic functions are vanilla functions -- they just happen to have some extra magic beneath the surface.
Let us create a flock of different animals answering to a single "speak" generic function which will do appropriately varied things depending on the class of the argument:
root: defclass: Dog ; root: defclass: Poodle :isA 'Dog ; 'Poodle makeInstance --> _poodle root: defclass: Sheltie :isA 'Dog ; 'Sheltie makeInstance --> _sheltie root: defclass: Bulldog :isA 'Dog ; 'Bulldog makeInstance --> _bulldog root: defclass: Bird ; root: defclass: Wren :isA 'Bird ; 'Wren makeInstance --> _wren root: defclass: Sparrow :isA 'Bird ; 'Sparrow makeInstance --> _sparrow root: defclass: Crow :isA 'Bird ; 'Crow makeInstance --> _crow root: defgeneric: speak { $ -> } ; ( One input, no return values. ) root: defmethod: speak { 'Bird } pop "Chirp!\n" , ; root: defmethod: speak { 'Crow } pop "Caw!\n" , ; root: defmethod: speak { 'Dog } pop "Woof!\n" , ; root: defmethod: speak { 'Poodle } pop "Yip!\n" , ; root: _poodle speak Yip! root: _sheltie speak Woof! root: _bulldog speak Woof! root: _wren speak Chirp! root: _sparrow speak Chirp! root: _crow speak Caw! root:
Things to note:
_wren
and _sheltie
to speak
, even though we didn't explicitly specify a speak
method for them: They inherit the appropriate behavior from
(respectively) Bird
and Dog
.
_crow
and _poodle
behave differently from their
ancestral Bird
and Dog
classes because the default
behavior has been explicitly over-ridden.
Dog
and Bird
have no common ancestor, but speak
still works fine on both of them. You cannot do that in C++ or Java,
but it is very useful in real systems where existing inheritance trees
sometimes do not fit what you need to do today.
Go to the first, previous, next, last section, table of contents.