moved generic.txt to devel-guide
parent
7f92f0df6a
commit
6f765bc74c
217
doc/generic.txt
217
doc/generic.txt
|
@ -1,217 +0,0 @@
|
|||
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 systsem 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 tuple metaclass
|
||||
|
||||
This is quite involved; see tuples.txt.
|
Loading…
Reference in New Issue