# Generic words -- a new sub-paradigm for concatenative languages

## Slava Pestov — 2005-01-02 05:16:17

    Hi all,

    I believe generic words represent an important extension of the

    concatenative paradigm. They have allowed a lot of code to be

    considerably simplified and cleaned up in the Factor library. I have

    written up a document describing the system. It is current as of Factor

    0.71, but applies to 0.70 (latest version on web site) as well.

    Feedback is welcome.

    ------------->8-------------

    THE FACTOR GENERIC WORD SYSTEM

    Factor's generic word system is a very abstract generalization of

    "object oriented" features found in other programming languges.

    To use the generic word system, you must put the following near the

    beginning of your source file:

    USE: generic

    The key motivation is that sometimes, you want to write a word that has

    differing behavior depending on the class of its argument. For example,

    in a game, a 'draw' word could take different action if given a ship, a

    weapon, a planet, etc.

    Duplicating 'type case' logic is undesirable and also results in

    unnecessary coupling -- adding support for a new type of graphical

    object would require modifying the original definition of 'draw', for

    example.

    * Types

    In Factor, the idea of a 'type' refers to a very concrete concept. The

    type of an object is its representation in runtime object memory. Types

    include fixnums, bignums, cons cells, vectors, strings, and so on. The

    set of available types is fixed; adding a new type requires modifying

    the runtime source written in C.

    * Classes

    In Factor, a 'class' is just a predicate that categorizes objects as

    being a member of the class or not. To be useful, it must be consistent

    -- for a given object, it must always return the same truth value.

    Examples of classes might include:

    - Cons cells where both elements are integers

    - Floating point numbers between -1 and 1

    - Hash tables holding a certain key

    - Any object that occurs as a member of a certain global variable

    holding a list.

    - ... and so on.

    As you can see, a class of objects does not need to be a subset or a

    superset of a type of objects.

    Classes, unlike types, can be defined by the user.

    * Generic words

    A generic word is a word whose behavior depends on the class of the

    object at the top of the stack.

    Generic words are defined using the following syntax:

    GENERIC: draw ( actor -- )

    #! Draw the actor.

    A stack effect comment, as above, is not required but recommended.

    * Methods

    A method associates behavior to a generic word. Methods are defined by

    writing M:, followed by a class name, followed by the name of a

    previously-defined generic word.

    One of the main benefits of generic words is that each method definition

    can potentially occur in a different source file. Generic word

    definitions also hide conditionals.

    Here are two methods for the generic 'draw' word:

    M: ship draw ( actor -- )

         [

             surface get screen-xy radius get color get

             filledCircleColor

         ] bind ;

    M: plasma draw ( actor -- )

         [

             surface get screen-xy dup len get + color get

             vlineColor

         ] bind ;

    Here, 'ship' and 'class' are user-defined classes.

    * Metaclasses

    To understand what classes already exist, and how to define your own

    classes, the concept of a 'metaclass' must be grasped first. Roughly

    speaking, a metaclass is a class of classes.

    New metaclasses can be defined by the user, but its an involved process

    that requires a deeper understanding of the generic word system than

    can be given here.

    ** The object class

    Every object is a member of the object class. The object class is also a

    metaclass, and it is the one and only instance of itself.

    Confusing? The idea is pretty simple. If you define a method on

    'object', it will be called when no more specific method is available:

    GENERIC: describe

    M: number describe "The number " write . ;

    M: object describe "I don't know anything about " write . ;

    Since the only instance of the object metaclass is itself, you cannot

    define new classes in the object metaclass.

    ** The builtin metaclass

    The builtin metaclass contains precisely the following classes; each

    class corresponds to a runtime type:

    alien

    array

    bignum

    complex

    cons

    dll

    f

    fixnum

    float

    port

    ratio

    sbuf

    string

    t

    vector

    word

    Each builtin class has a corresponding membership test predicate, named

    after the builtin class suffixed with '?'. For example, cons?, word?,

    etc.

    Adding new classes to the builtin metaclass requires modifications to

    the C code comprising Factor's runtime.

    ** The union metaclass

    The union metaclass contains classes whose members are defined to be the

    aggregate of the members of a list of existing classes.

    For example, the Factor library defines some unions over numeric types:

    UNION: integer fixnum bignum ;

    UNION: rational integer ratio ;

    UNION: real rational float ;

    UNION: number real complex ;

    Now, the absolute value function can be defined in an efficient manner

    for real numbers, and in a more general fashion for complex numbers:

    GENERIC: abs ( z -- |z| )

    M: real abs dup 0 < [ neg ] when ;

    M: complex abs >rect mag2 ;

    New unions can be defined by you, and the numerical types example above

    gives the syntax: you write UNION: followed by the name of the union,

    followed by its members. The list of members is terminated with a

    semi-colon.

    A predicate named after the union followed by '?' is

    automatically-defined. For example, the following definition of 'real?'

    was automatically created:

    : real?

         dup rational? [

             drop t

         ] [

             dup float? [

                 drop t

             ] [

                 drop f

             ] ifte

         ] ifte ;

    ** The predicate metaclass

    The predicate metaclass contains classes whose membership test is an

    arbitrary expression. To speed up dispatch, each predicate must be

    defined as a subclass of some other class. That way predicates

    subclassing from disjoint builtin classes do not need to be

    simultaenously tested.

    The library/strings.factor module defines some subclasses of integer,

    classifying the different types of ASCII characters:

    PREDICATE: integer blank     " \t\n\r" str-contains? ;

    PREDICATE: integer letter    CHAR: a CHAR: z between? ;

    PREDICATE: integer LETTER    CHAR: A CHAR: Z between? ;

    PREDICATE: integer digit     CHAR: 0 CHAR: 9 between? ;

    PREDICATE: integer printable CHAR: \s CHAR: ~ between? ;

    Each predicate defines a corresponding predicate word whose name is

    suffixed with '?'; for example, a 'digit?' word is automatically

    defined:

    : digit?

         dup integer? [

             CHAR: 0 CHAR: 9 between?

         ] [

             drop f

         ] ifte ;

    For obvious reasons, the predicate definition must consume and produce

    exactly one value on the stack.

    ** The traits metaclass

    (The name for this metaclass is wrong and will change eventually. The

    original idea was to allow an object to inherit any number of 'traits',

    thus they would behave like mixins. This never materialized.)

    The traits metaclass allows one to associate more fine-grained behavior,

    specifically with hashtables.

    New classes can be defined like so:

    TRAITS: plasma

    In terms of behavior, this is actually identical to the following:

    PREDICATE: hashtable plasma \ traits swap hash plasma = ;

    However, it is far more efficient (and less verbose).

    You can define methods as usual:

    GENERIC: collide ( actor1 actor2 -- )

    M: plasma collide ( actor1 actor2 -- )

         #! Remove the other actor.

         deactivate deactivate ;

    How does one actually get an object that plasma? responds with t to? You

    define a constructor word by writing C: followed by the class name:

    C: plasma ( actor dy -- plasma )

         [

             velocity set

             actor-xy

             blue color set

             10 len set

             5 radius set

             active on

         ] extend ;

    The constructor word is named after the class, surrounded in angle

    brackets (< and >). For example, the above actually creates a word named

    <plasma>.

    The constructor's definition begins with the parameters given by the

    user, underneath a blank plasma object.

    That is, a dummy constructor just returns a blank hashtable that

    responds t to the corresponding membership predicate:

    TRAITS: foo

    C: foo ;

    <foo> foo? .

    ==> t

    "hello" foo? .

    ==> f