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


Object

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:


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