[REBOL] RFC on support for user-defined "types"
From: joel::neely::fedex::com at: 11-Jun-2002 10:34
IANARTE, but I suspect that the level of effort to allow users
to define additional DATATYPE! values might be non-trivial.
That assumption motivated me to wonder about whether there
could be some simpler enhancements that might still add value
to the development of reliable scripts, and led to the following
train of thought.
There are at least two obvious virtues to the ability to declare
data types for functional arguments:
1) Documentation -- the declarations make it easer for someone
who is reading the source for the function (or its HELP) to
understand what it expects/does.
2) Error handling -- an error in typing can be caught at the
point of the function invocation, rather than more deeply
(at some point where a type-inappropriate use is made of the
argument). This usually assists the process of finding,
understanding, and correcting the error.
It's nice to have the capability of detecting errors as early as
possible, and at the level of an expression that is most likely to
be recognizable (both as to location in the script and purpose).
AFAICT, the notation
somefunc: func [a [typex!] b [typey! typez!] ...] [
...
]
could be understood as an interpreter-mediated (and therefore
much faster, with more meaningful error messages) equivalent of
somefunc: func [a b ...] [
if not typex! = type? a [
;; complain about type of parameter a
]
if none? find reduce [typey! typez!] type? b [
;; complain about type of parameter b
]
...
]
A truly extensible language allows the programmer to define new
types as needed for a convenient "conceptual vocabulary" for
expressing the ideas of specific applications. Mainstream OO
languages usually handle this via the concept of "class", so that
each new class serves as a user-defined type. Argument checking
can include the notion of the class (or superclass) of which an
argument must be an instance.
Oooops! Wait! REBOL objects have no class!
This means that the best we currently can do for object arguments is
somefunc: func [a [object!] b [object!] ...] [
...
]
even when the programmer has specific kinds/flavors of objects in
mind as the function is designed.
However...
REBOL does make use of the idea that one can access an attribute
(e.g., invoke a method) of an object by name, so that any proposed
use of an object may very well have expectations about a (minimum)
set of attributes that the object should possess. To continue with
my hypothetical example, the programmer may expect to use an INVERT
method on the first parameter, and a MEMORIZE method on the second:
somefunc: func [a [object!] b [object!] ...] [
...blah blah blah...
... a/invert ...
... b/memorize ...
]
and it would be quite nice to be told
Excuse me, but you invoked SOMEFUNC with an inappropriate
object as its first argument!
instead of a message showing only some fragment of the innards of
SOMEFUNC that might be unclear as to purpose and hard to locate.
Therefore, I propose for discussion the idea of extending a function
specification to allow a block of words after an OBJECT! type
specifier, with the meaning that the corresponding argument would be
required possess all of the indicated words within its context.
In other words, we'd be specifying (a portion of) the *interface* that
an object should possess, rather than a (non-REBOL) notion of "class".
This would allow the above hypothetical function definition to be
written as:
somefunc: func [
a [object! [invert]]
b [object! [memorize]]
...
][
...blah blah blah...
... a/invert ...
... b/memorize ...
]
to document/enforce that the first argument must possess an INVERT
attribute/method and the second must have a MEMORIZE attribute/method,
with the checking to be done by the interpreter at the/each point of
function invocation (e.g., as type checking is done now).
Feedback, comments, etc. welcome as always!
-jn-