TEACH OBJECTCLASS_EXAMPLE                     Aaron Sloman 11 April 1992
                   (Based partly on TEACH FLAVOURS and HELP OBJECTCLASS)

This gives some illustrative examples of the use of LIB OBJECTCLASS,
which is described in more detail in HELP * OBJECTCLASS.

SEE LIB * OBJCLASS_EXAMPLE for the full listing of the example code
discussed in this tutorial.

CONTENTS - (Use <ENTER> g to access sections)

 -- Getting started
 -- Introduction
 -- Object Oriented Programming
 -- Some Syntax
 -- Example: defining objectclass person
 -- Procedures created automatically by define :class
 -- Four ways of creating instances
 -- -- (1) Using consperson
 -- -- (2) Using newperson
 -- -- (3) Using instance ... endinstance
 -- -- (4) Using define :instance
 -- Slots, or instance variables, and the field accessors
 -- Recogniser procedures
 -- Defining Methods
 -- Defining subclass adult of person
 -- Circular Structures and Printing
 -- The marry method
 -- A slightly different definition of "marry"
 -- Improving the printing of adults
 -- Inheritance
 -- The professor objectclass
 -- Multiple Inheritance
 -- The Ordering of Components
 -- Daemons
 -- Example using call_next_method
 -- Updater Methods
 -- Wrappers
 -- Computing slot defaults at compile time
 -- Methods can have untyped arguments
 -- Metaclasses
 -- Bugs and Omissions
 -- See Also

-- Getting started ----------------------------------------------------

To make the package described in this teach file available you need to do

    lib objectclass
or
    uses objectclass

After that you will be able to run the examples given below, e.g. using
the "mark and load" facilities described in TEACH *MARK and TEACH *LMR.



-- Introduction --------------------------------------------------------

For an introduction to the OBJECTCLASS package and a discussion of some
of its features see HELP * OBJECTCLASS.

Compare TEACH * FLAVOURS if you wish to find out about the FLAVOURS
package. This file is partly modelled on it to provide a basis for
comparison.


-- Object Oriented Programming -----------------------------------------

TEACH FLAVOURS gives an introduction to object oriented programming and
some of the key concepts involved.


-- Some Syntax ---------------------------------------------------------

In the Objectclass package, new classes are introduced using the
define-syntax with the keyword ":class" after "define". The syntax
is broadly modelled on that of recordclass, since objects are records.

A simplified schema for defining a new object class is

    define :class <classname>;
        is <superclass1> <superclass2>;
        slot <slot1> = <expr1>;
        slot <slot2> = <expr2>;
        ...
    enddefine;

Methods that perform actions on instances of that class are defined using
the syntax

    define :method <name>(<var>:<classname>);
        ...actions...
    enddefine;

Examples of objectclass definitions and method definitions are given
below.

The full range of options that can be used is explained in the files
REF * OBJECTCLASS * DEFINE_CLASS * DEFINE_METHOD.


-- Example: defining objectclass person -------------------------------

We are going to start by defining a class called "person", which will
represent people whose age, name and sex interests us. Later we shall
define subclasses of people with additional features of interest,
including adults (who are capable of having spouses) and professors,
(who have subjects, and who sometimes retire).

Here is a simple definition for an objectclass to represent people. We
assume each person has a name, an age and a sex. And we specify default
values for these attributes.

    define :class person;
        slot person_name = undef;
        slot person_age  = 0;
        slot person_sex  = undef;
    enddefine;

STYLISTIC WARNING: if you define an objectclass like this with short
attribute names, e.g. "name" instead of "person_name" and "age" instead
of "person_age" then you are likely later on to forget that you must not
use the same identifier for a different purpose. Thus for safety start
all attribute names with a reminder of the class to which they are
relevant. This will be more verbose, but less likely to lead to errors.
(Using sections could reduce the risk of error due to name clashes. See
HELP * SECTIONS)


-- Procedures created automatically by define :class ------------

Like recordclass, the :class declaration creates a lot of
procedures.  In fact, it creates a lot of the same procedures and gives
them the same names as recordclass does. See HELP * RECORDCLASS. (The
"defclass" construct is more general, but as it was added to the
language later, more Pop-11 users may be familiar with recordclass. See
HELP * DEFCLASS for details.)

Firstly, :class creates a collection of constructors and
destructors. If you've used recordclass then you'll have expected
"consperson" and "destperson".  The contructors are described in more
detail in the next section on creating instances.

    vars P, Pname, Page, Psex;
    consperson("fred", 5, "male" ) -> P;
        ;;; creates person P

    P =>
    ** <person person_name:fred person_age:5 person_sex:male>

    destperson( P ) -> (Pname, Page, Psex);
        ;;; extracts the person_, age and sex from a person instance

    Pname =>
    ** fred

    destperson(P) =>
    ** fred 5 male


-- Four ways of creating instances ------------------------------------

There are four ways of creating an instance of an objectclass, using the
cons... and new... procedures corresponding to the objectclass, and the

    instance ... endinstance

    define :instance ... enddefine

syntax forms described below.


-- -- (1) Using consperson

The cons... procedure, is analogous to the constructor produced by
recordclass, and must be given the values to be assigned to slots.

For example:

    vars adam = consperson("adam", 24, "male");
    adam =>
    ** <person person_name:adam person_age:24 person_sex:male>

    isperson(adam) =>
    ** <true>


-- -- (2) Using newperson

    newperson() -> P;
        ;;; constructs a new default person

    P =>
    ** <person person_name:undef person_age:0 person_sex:undef>


-- -- (3) Using instance ... endinstance

If you want to create a male person aged 77, and don't yet know the name
you can do this:

vars ppp;

    instance person;
        person_age = 77;
        person_sex = "male"
    endinstance -> ppp;

    ppp=>
    ** <person person_name:undef person_age:77 person_sex:male>


-- -- (4) Using define :instance

A minor variation on the -instance- syntax is the define:instance syntax.
This allows you to invent an instance and bind it to a variable at the
same time.

    define :instance xxx:person;
        person_age = 77;
        person_sex = "male"
    enddefine;

    xxx =>

The -instance- syntax is included to help make programs creating instances
with lots of non-default slots more attractive.  A detailed description
of the -instance- syntax is provided in

    REF * INSTANCE * DEFINE_INSTANCE.


-- Slots, or instance variables, and the field accessors --------------

"define :class" creates three slots or instance variables.  In
this documentation we shall call them slots or fields but most other
OOPS (including the FLAVOURS documentation) call them "instance
variables", following the names establish by SmallTalk. The objectclass
slots have corresponding accessing procedures, with the same names.

    newperson() -> P;
    P =>
    ** <person person_name:undef person_age:0 person_sex:undef>

    person_name( P ) =>
    ** undef

    "mary" -> person_name( P );
    person_name( P ) =>
    ** mary

    16 -> person_age( P );
    person_age( P ) =>
    ** 16

    "female" -> person_sex( P );
    person_sex( P ) =>
    ** female

    P =>

In other object-oriented packages you might need to send a message
"name" to P in order to get the name, e.g., in FLAVOURS

    P <- person_name -> x;

But this will not work with the objectclass package.

Objectclass simply uses procedures instead of messages. The procedures
associated with an objectclass are often referred to as "methods". Thus
there is a method (with an updater method) associated with each slot or
field in an objectclass.


-- Recogniser procedures ----------------------------------------------

"define :class" also creates recogniser procedures. "isperson" is
a recogniser that returns true for persons and for any sub-classes.

    isperson( P ) -> bool          recognises persons and sub-classes

    isperson(P) =>
    ** <true>

    isperson(99)=>
    ** <false>


-- Defining Methods ---------------------------------------------------

In order to perform operations on instances of objectclasses, users can
define additional methods (i.e. additional to the ones created
automatically).

Objectclass methods are just procedures that act differently
depending on the types (keys) of their arguments. Although there is
complete freedom to define methods to do completely different things to
different classes of objects (e.g. persons and trees) it is a good idea
to try to make them at least roughly analogous. Compare the way the
Pop-11 operator "<>" can be applied to words, lists, strings,
procedures, etc.

Different definitions of a method are needed for each combination of
objectclasses. For example, you could write a method for giving a person
a birthday.


    define :method birthday( p:person );
        lvars p, age;
        person_age(p) + 1 -> age;
        age -> person_age(p);
        [Happy birthday ^(person_name(p)) - now aged ^age] =>
    enddefine;

    vars joe = consperson("joe", 0, "female");
    joe =>
    ** <person person_name:joe person_age:0 person_sex:female>

    birthday(joe);
    ** [Happy birthday joe - now aged 1]

    person_age(joe) =>
    ** 1

    birthday(joe);
    ** [Happy birthday joe - now aged 2]

HELP OBJECTCLASS gives additional examples of method definitions.


-- Defining subclass adult of person ----------------------------------

When people grow up they may acquire a spouse. So we define a new
subclass of persons, called adults, who are able to have a spouse. But
the spouse may be nonexistent, which we'll make the default.

    define :class adult;
        is person;                      ;;; specify superclass
        slot person_spouse = false;     ;;; and additional slot
    enddefine;

Because of the second line, the adult objectclass is a subclass of the
person objectclass. However, every instance of adult is also an instance
of person, though not vice versa. For example

    define :instance adam:adult;
        person_name = "adam";
        person_age = 33;
        person_sex = "male";
    enddefine;

    define :instance eve:adult;
        person_name = "eve";
        person_age = 35;
        person_sex = "female";
    enddefine;

    define :instance dot:adult;
        person_name = "dot";
        person_age = 25;
        person_sex = "female";
    enddefine;

Try these print commands

    adam =>
    dot=>

The printing is rather verbose. We'll see below how to limit it.


-- Circular Structures and Printing -----------------------------------

We shall shortly define a method for linking two adults in marriage, but
before doing that we need to do something about the fact that if two
individuals are spouses of each other then the print routine might
get stuck in a loop.  Fortunately, the default printing routine
checks for recursive loops.

Just to check that it does not get stuck in printing loops, we can
do this:

    vars
        a1 = instance adult person_name = "a1" endinstance,
        a2 = instance adult person_name = "a2" endinstance;

    a1 -> person_spouse(a2);
    a2 -> person_spouse(a1);

Now there's a loop as a1 is the spouse of a2, and a2 is the spouse of
a1. Will the printing get stuck in a loop?

    a1 =>
    ** <adult person_name:a1 person_age:0 person_sex:undef person_spouse:
     <adult person_name:a2 person_age:0 person_sex:undef person_spouse:(LOOP
     <adult>)>>

    a2 =>
    ** <adult person_name:a2 person_age:0 person_sex:undef
     person_spouse:<adult person_name:a1 person_age:0 person_sex:undef
     person_spouse:(LOOP <adult>)>>

It's rather verbose. Later we'll offer a better method for printing adults.
Now we can define the marry method.


-- The marry method ---------------------------------------------------

First we define a couple of subroutines to check legality and perform
the actions we need.

    define check_bigamy(p1, p2);
        lvars p1, p2, spouse;
        person_spouse(p1) -> spouse;
        if spouse then
            ;;; check p1 is not already married to someone else
            unless spouse == p2 then
                mishap('BIGAMY', [% p1, p2 %]);
            endunless
        endif
    enddefine;

    define take_spouse(p1, p2);
        lvars p1, p2;
        ;;; update my spouse slot
        p2 -> person_spouse(p1);
        ;;; take the vows
        [I ^(person_name(p1))
            take thee ^(person_name(p2))
            to be my lawfully wedded
            other] =>
    enddefine;

    define :method marry( p1:person, p2:person );
        lvars p1, p2, spouse1, spouse2;

        ;;; see if p1 and p2 are of the same sex
        if person_sex(p1) == person_sex(p2) then
            [hmm very modern] =>
        endif;

        check_bigamy(p1, p2);
        check_bigamy(p2, p1);

        take_spouse(p1, p2);
        take_spouse(p2, p1);
    enddefine;


Having defined the method we can now use it:

    marry(adam, eve);
    ** [I adam take thee eve to be my lawfully wedded other]
    ** [I eve take thee adam to be my lawfully wedded other]

    person_name(person_spouse(adam)) =>
    ** eve
    person_name(person_spouse(eve)) =>
    ** adam

What if adam tries to marry dot?
    marry(adam, dot);

;;; MISHAP - BIGAMY
;;; INVOLVING:  '<person name:'adam' age:'33' sex:'male' spouse:''<person
     name:'eve' age:'35' sex:'female' spouse:' ...'>''>' '<person name:'dot'
     age:'25' sex:'female' spouse:'<false>'>'
;;; DOING    :  sysprmishap mishap check_bigamy ....


Is adam the spouse of his spouse?
    person_spouse(person_spouse(adam)) == adam =>
    ** <true>


-- A slightly different definition of "marry" -------------------------

In an object oriented system that uses message sending, a different
style of definition of marry would be more natural. It is illustrated
by the marry method shown in TEACH * FLAVOURS/marry as part of the
definition of flavour person. A close translation of that approach using
objectclass would be the following.

    define :method marry( p1:person, p2:person );
        lvars p1, p2, spouse;

        ;;; see if p1 and p2 are of the same sex
        if person_sex(p1) == person_sex(p2) then
            [hmm very modern] =>
        endif;

        person_spouse(p1) -> spouse;
        if spouse then
            ;;; check p1 is not already married to someone else
            unless spouse == p2 then
                mishap('BIGAMY', [% p1, p2 %]);
            endunless
        else
            ;;; not already married. Fix it
            ;;; update my spouse slot
            p2 -> person_spouse(p1);
            ;;; take the vows
            [I ^(person_name(p1)) take thee ^(person_name(p2))
                to be my lawfully wedded other] =>

            ;;; Now make sure the other person does the same
            marry(p2, p1)
        endif
    enddefine;

If you try using this method to marry two people of the same sex it
prints out an extra line. Why? Because it calls itself recursively
whereas the first version doesn't. This makes the first version a bit
more efficient. The second definition of method marry could add a test
before starting the recursive call.

Apart from that the two versions are functionally equivalent.


-- Improving the printing of adults ----------------------------------

We can improve the printing of adults by having only the NAME of the
spouse printed out.

    define :method print_instance(a:adult);
        lvars a, spouse;
        person_spouse(a) -> spouse;

        printf(
            '<adult name:%P age:%P sex:%P spouse:%P>',

            [%
                person_name(a),
                person_age(a),
                person_sex(a),
                if spouse then person_name(spouse) else false endif
            %])
    enddefine;

(See HELP * PRINTF if necessary).

Now test it

    adam =>
    ** <adult name:adam age:33 sex:male spouse:eve>


-- Inheritance --------------------------------------------------------

See TEACH * INHERITANCE for more details.

The objectclass adult inherited the person_age, person_name and
person_sex slots from objectclass person, and added a new slot of its
own, person_spouse.

We can now define new objectclass professor, a subclass of adult, to
inherit slots and methods from adult and person.

We could make an objectclass definition with slots such as name, age,
sex, spouse, telephone_number, discipline and then copy the methods we
have already defined for the person objectclass and add the new ones for
the professor-specific actions.  This would be a rather painful way of
going about things.  It would also mean that if we were to want to
change the actions of the marriage method, we would have to change it
both for the adult objectclass and for the professor objectclass (and
any other classes that we have defined for special classes of adult).
What we want to say is that professors are special kinds of people that
have all the attributes of adults (and perhaps some others as well) and
are capable of being dealt with by all the methods that apply to persons
and adults as well as perhaps some special methods specific to
professors.  The terminology for this is that the professor objectclass
"inherits" from the adult and person objectclasses. We then say that
"adult" and "person" are superclasses of "professor".

This process of defining a new subclass of a pre-existing class is
called "specialisation".


-- The professor objectclass ------------------------------------------

We are now ready to define a professor as an adult with additional
attributes.

    define :class professor;
        is adult;                   ;;; superclass
        slot telephone_number;      ;;; two new slots
        slot discipline;
    enddefine;

This time we have not specified the default values for the new slots, so
let's see what the default is:

    vars roger = newprofessor();
    "penrose" -> person_name(roger);
    telephone_number(roger) =>
    ** undef

    discipline(roger) =>
    ** undef

If a slot value is unspecified it defaults to -undef-.  (See
TEACH *SLOT_DEFAULTS for exact information of defaulting.)


Let's see how a professor is printed out:

    roger =>
    ** <adult name:penrose age:0 sex:undef spouse:<false>>

That's a bit clumsy, so we can define a special printing method for
professors.

    define :method print_instance( p:professor );
        lvars p;
        pr('<professor '); pr(person_name(p)); pr('>');
    enddefine;

Who cares about the age, sex and spouse of a professor?

We can test this on an example:

    roger =>
    ** <professor penrose>

Additional methods specific to professors are possible. First lets
introduce a new object class for subjects on which professors can write:

    define :class subject;
        slot subject_name = 'undecided';
    enddefine;

Here's how a professor writes a paper on a subject.

    define :method write_paper(p:professor, s:subject);
        lvars p, s;
        [^(person_name(p))
            is writing a paper on ^(subject_name(s))] =>
        /* now insert code for a professor to write a paper */
    enddefine;

Here's how a professor writes a paper on another professor.

    define :method write_paper(p:professor, s:professor);
        lvars p, s;
        [^(person_name(p))
            is writing a paper criticising ^(person_name(s))] =>
        /* now insert code for a professor to write a paper */
    enddefine;


    vars rel =
        instance subject; subject_name = "relativity" endinstance;

    rel =>
    ** <subject subject_name:relativity>

    write_paper(roger, rel);
    ** [penrose is writing a paper on relativity]


Now get him to write about a professor called jones:

    write_paper(roger,
        instance professor; person_name = "jones" endinstance );
    ** [penrose is writing a paper criticising jones]

So a professor has all the instance variables that a person has as  well
as the  two extra  ones specified.  It also  means that  a professor  is
receptive to all the methods  that a person or an  adult is, as well  as
the extra method -write_paper- which was (rather poorly) defined  above,
for at least two cases.

    vars marvin;
    consprofessor("marvin", 53, "male", "nina", '(691) 455 554',
        "computers_and_philosophy") -> marvin;

    marvin =>
    ** <professor marvin>

    discipline(marvin) =>
    ** computers_and_philosophy

    birthday(marvin);
    ** [Happy birthday marvin - now aged 54]

    person_age(marvin) =>
    ** 54

    write_paper(marvin,conssubject("ai"));
    ** [marvin is writing a paper on ai]

    write_paper(marvin, roger);
    ** [marvin is writing a paper criticising penrose]


-- Multiple Inheritance ------------------------------------------------

In many "object oriented" systems, though not all, you can specify that
an objectclass should inherit from more than one objectclass.  This
feature is called multiple inheritance.

Suppose that you wanted to create a special class of french professors.
You could create an objectclass called french_professor which inherits from
professor and defines all the features (instance variables and methods)
that are special to french professors.

    define :class french_professor;
        is professor;
        /* features special to french professors */
    enddefine;

Alternatively you could define a french person objectclass which
encompasses the features of frenchness and then mix the professor
objectclass with the french person objectclass to create an objectclass
which captures the features of frenchness and the the features of
professordom.  Given a french person objectclass you can create the
french professor objectclass by doing:

    define :class french_professor;
        is professor, french_person;
        /* features special to french professors that are not true
            of professors in general or french people in general
        */
    enddefine;

The advantages of creating a french professor by this second method is
that you can keep all features of french people together and quite
separate from details which are only true of french professors.  More
importantly you now have an objectclass for a french person that you can
mix with other objectclasses. For example if you define a student objectclass
you can easily create a french student objectclass.  If you do this then
you can change some detail about french people and both the french
professor objectclass and the french student objectclass will change
automatically.  In this sense the french person objectclass is a useful
placeholder.

Multiple Inheritance needs to be used with caution. For example, if you
wanted to create a good toy truck objectclass, you could not simply
start with the truck objectclass and toy objectclass and combine their
features. Similarly, it is not possible to have a "good thing"
objectclass and combine it with teacher and truck objectclasses to
obtain good teacher and good truck objectclasses.

REF *OBJECTCLASS/'Mixins' gives additional information on inheritance
techniques.


-- The Ordering of Components ------------------------------------------

Multiple inheritance can lead to a complex network, or lattice, or
objectclass components from which an objectclass might inherit instance
variables and methods.  For example the objectclasses described above
might be part of a larger collection with something like the following
structure.

                          /-------\
                          | ANIMAL|
                          \+-+-+-+/
                 +---------+ | | |
              /--+---\ +-----+ | |
              |PERSON| |       | +----+
              \-+--+-/ |       |      |
                |  |   |       |     /+---------\
                |  +---|-----+ |     |CHIMPANZEE|
                |      |     | |     \----------/
              /-+------+\   /+-+----------\
              |PROFESSOR|   |FRENCH PERSON|
              \------+--/   \-+-----------/
                     |        |
                     |        |
                  /--+--------+----\
                  |FRENCH PROFESSOR|
                  \----------------/

The rules for inheritance in such cases are described in the
TECAH *INHERITANCE.


-- Daemons -------------------------------------------------------------

Sometimes an objectclass will want not only to use the method defined in
one of its components but also to do some other action.  For example if
you have an object representing part of a diagram on a screen it may
have parts of the diagram attached to it for which it is responsible.
If the object receives a message to move then it will want to pass the
message on to the parts of the diagram for which it is responsible.

Another example might be that you want professors to check if they have
reached retirement age whenever they get a "birthday" message.

In both of these cases you could do this by redefining the original
method for the subclass in question, but that would be wasteful. Instead
you want to be able to say what happens to a subclass after (or before)
a particular method inherited from a superclass is run, while leaving
the inherited method unchanged, and preserving modularity of design.

There are two ways that object oriented programming systems achieve this
functionality.  The first, adopted here is to have some syntax for
saying within a method definition "now go and do what you would have
done had this method not been here". The objectclass package uses the
call_next_method syntax, illustrated below. (Some systems use a name
something like "sendsuper" rather than "call_next_method")

The alternative approach, followed in the Pop-11 FLAVOURS package, as
explained in TEACH * FLAVOURS, is to use "before" and "after" methods,
sometimes called "daemons".


-- Example using call_next_method -------------------------------------

    define :method birthday(prof:professor);
        lvars prof, age, name = person_name(prof);

        call_next_method(prof);

        person_age(prof) -> age;

        if age >= 65 then
            [^name is now retired] =>
        elseif 65 - age < 5 then
            [^name has only  ^(65 - age) years before retiring] =>
        endif;
    enddefine;

    define :instance albert:professor;
        person_age = 63;
        person_name = "einstein";
        discipline = "relativity";
    enddefine;

    albert =>
    ** <professor einstein>

    birthday(albert);
    ** [Happy birthday einstein - now aged 64]
    ** [einstein has only 1 years before retiring]

    birthday(albert);
    ** [Happy birthday einstein - now aged 65]
    ** [einstein is now retired]

    birthday(albert);
    ** [Happy birthday einstein - now aged 66]
    ** [einstein is now retired]

REF * CALL_NEXT_METHOD explains in more detail how this works.

Just to check that instances that are not professors don't get this
treatment:

    vars margaret =
        instance person;
            person_name = "margaret";
            person_age = 63
        endinstance;

    birthday(margaret);
    ** [Happy birthday margaret - now aged 64]

    birthday(margaret);
    ** [Happy birthday margaret - now aged 65]


-- Updater Methods ----------------------------------------------------

The objectclasses package, like POP-11 itself, recognises a distinction
between methods per se and methods invoked in the updater position. See
HELP * DEFINE_METHOD

For example the following are distinguished in that the first one runs
the person_age method, and the second runs the person_age updater
method.

    person_age(adam) -> x;

    x -> person_age(adam);


-- Wrappers -----------------------------------------------------------

Wrappers are procedures that you associate with classes that can
intercept construction, slot access, and slot update.  You introduce
wrappers with the following syntax

    vars gender;

    ;;; A simple construction-wrapper
    define making_new_animal() -> item;
        lvars item = apply();        ;;; construct the animal
        nprintf(
            'Hooray, it\'s a %p',
            [% gender( item ) == "male" and "boy" or "girl" %]
        )
    enddefine;

    ;;; Animals use this wrapper.
    define :class animal;
        slot gender = "female";
        on cons do making_new_animal;
    enddefine;

    newanimal() =>
    Hooray, it's a girl
    ** <animal gender:female>

Instead of the word "cons" in the above example, you could use "access"
to define a wrapper which is called when a slot is accessed or updated.

Wrappers are inherit and are cumulative.  At the moment, there is no way
of avoiding the inheritance of your parent's wrappers.  This is a
restriction that I plan to remove at a later date, together with the
restriction that a class inherits all slots, too.

-- Computing slot defaults at compile time ----------------------------

When you specify a default for a slot, using

    define :class <classname>;
        slot <slot> = <default>;
        ...
    enddefine;

The <default> expression is computed whenever an instance is created.  This
is very general.  However, sometimes you would like to specify a default that
is calculated at compile-time.  This is sufficiently common that there is a
special syntax for it: simply use "==" in place of the "=" sign.

By writing
    <slot> == <default expr>
the <default expr> is computed at compile time, i.e. when the definition is
read in. The resulting value is then used as the default value for the slot
when an instance is created.

See TEACH * SLOT_DEFAULTS for a more extensive explanation.


-- Methods can have untyped arguments ---------------------------------

It is possible to define methods which mix typed and untyped arguments.
For example someone who wishes to fake a different age might use this
method, where the untyped argument "years" represents a pop-11 integer.

    define :method get_younger(years, p:person);
        lvars years, p, age = person_age(p);

        age - years -> age;
        age -> person_age(p);

        [^(person_name(p)) now has age ^age] =>
    enddefine;

    birthday(albert);
    ** [Happy birthday einstein - now aged 67]
    ** [einstein is now retired]

    get_younger(10, albert);
    ** [einstein now has age 57]

    birthday(albert);
    ** [Happy birthday einstein - now aged 58]

For more on this see HELP * DEFINE_METHOD


-- Metaclasses --------------------------------------------------------

Some OOP systems have special classes called metaclasses whose instances
are themselves classes. In LIB FLAVOURS these are called metaflavours.

In the objectclass package metaclasses are not provided. For more on
this see HELP * LIKE_FLAVOURS. The call_next_method and other facilities
provide an alternative approach to some of the features of metaclasses.


-- Bugs and Omissions --------------------------------------------------


-- See Also ------------------------------------------------------------

TEACH * OOPS
    For a general discussion of object oriented programming.

HELP  * RECORDCLASS
REF   * DEFSTRUCT
    These are worth reading since they are the facilities on which
    objectclass is closely based.

TEACH * INHERITANCE
TEACH * OBJ_INDEX
    List of teach files available

HELP  * OBJECTCLASS
    A more detailed overview
HELP  * OBJ_INDEX
    List of help files available

REF  * OBJECTCLASS
REF  * OBJ_INDEX
    List of REF files available


The following gives information about other OOP facilities in Poplog.

HELP  * NEWOBJ
TEACH * FLAVOURS
    This file also includes a useful overview of object oriented
    programming, and ends with a section on OOP jargon and a short
    bibliography
HELP  * FLAVOURS
REF   * FLAVOURS

The Common Lisp Object System (CLOS, pronounced "kloss" by some people
and "seeloss" by others!) is described in the second edition of
    G.L. Steele,
        Common Lisp: The Language
        Digital Press
        Burlington, MA 01803
--- C.all/lib/proto/objectclass/teach/objectclass_example
--- Copyright University of Sussex 1993. All rights reserved.