# My proposal for polymorphic/generic/overloaded user functions in Joy

## John Cowan — 2001-03-31 04:42:05

    Right now, Joy primitive words are often polymorphic/generic/overloaded; for

    example, "concat" applies equally to a pair of strings, sets, or lists.

    However, although Joy *allows* the creation of user-level types, the

    underlying type is almost always list, and so it is necessary to use a

    Lisp/Scheme style of naming functions: a distinct name for each operation

    on each type.

    My proposal involves continuing to define such distinctly named functions,

    but allowing them to be invoked by generic names.  Thus, if we have

    defined words foo-do, bar=do, and baz-do to "do something" to the user-level

    types foo, bar, and baz, we can write a word "do" which will invoke foo-do,

    bar-do, or baz-do based on whether the top stack item is a foo, a bar, or

    a baz, and in such a way that "do" does not have to know about foo, bar, and

    baz (and therefore continues to work even if zam-do is added to the system).

    The mechanism for deciding which data are foos, bars, or bazzes is entirely

    up to the programmer.  Let us assume without loss of generality that

    a foo is a list whose first member is the word foo, and likewise for

    bars and bazzes.  Then the predicate "type", defined as

            DEFINE type == dup first unitlist.

    will push the type of the top argument, wrapped in a unit list.  Now we

    are ready to introduce a novel combinator, which I will tentatively call

    "dispatch".

    "Dispatch" takes one quotation argument; however, the quotation is *not*

    executed.  Instead, the strings naming the words of which it consists

    are concatenated in reverse order with "-" between them, and the symbol

    table is searched for a defined function with that name.  If it exists,

    the stack is popped and it is executed.  If no such definition exists,

    the stack is unconsed and we try again.  When the stack top is a null

    list, "dispatch" signals an error.

    Now we can define the generic/polymorphic function "do":

            DEFINE do == type [do] swoncat dispatch.

    What happens now when a user program invokes "do" with a foo on the stack?

    When "dispatch" is executed, "[do foo]" is just above the foo object,

    and the result is that the "foo-do" function is executed.

    Suppose there is no "foo-do" function, and we want in that case to invoke

    "baz-do" instead, because foos are a subtype of bazzes?  In that case,

    "do" is on the stack and the word "foo" is invoked.  If its definition is

    	DEFINE foo == [baz] cons dispatch.

    then "dispatch" will see a "[do baz]" on the stack and will execute baz-do.

    This mechanism provides for simple OOP, but is obviously capable of great

    flexibility.   Any predicate whatever can stand in the place of "type".

    Creating lists longer than two elements for "dispatch" to work on

    allows the equivalent of inner types:  "[do inner outer] dispatch" will

    search in turn for outer-inner-do, outer-inner(with do on the stack),

    and outer (with do and inner on the stack) functions.  The generic

    functions can check for several possibilities before dispatching,

    and the type functions (like "foo" above) can do likewise, giving the

    equivalent of multiple inheritance.

    Comments?

    --

    John Cowan                                   cowan@...

    One art/there is/no less/no more/All things/to do/with sparks/galore

    	--Douglas Hofstadter