1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
% This file is part of Oaklisp.
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% The GNU GPL is available at http://www.gnu.org/licenses/gpl.html
% or from the Free Software Foundation, 59 Temple Place - Suite 330,
% Boston, MA 02111-1307, USA
\chapter{Types and Objects} \label{types}
Oaklisp is an object-oriented language which is organized around the
concept of \emph{type}. The type of an object determines its behavior
when operations are performed on it. To permit the modular
specification of types with complex behaviors, a type is allowed to
have multiple supertypes. There is no distinction in Oaklisp between
predefined system types and user-defined types.
A type specifies the behavior of an object by providing methods that
are used to perform operations on that object. Because methods are
inherited from supertypes, a subtype only needs to supply those
methods which are required to distinguish itself from the more general
types. A method defined for a given type pre-empts any inherited
methods for the same operation.
Instance variables are the mechanism for keeping state in objects.
Every object possesses a data structure where the values of its
instance variables are stored. Although each object contains storage
for all of the instance variables required by its type and supertypes,
methods for a given type can only refer to instance variables defined
in that type. In particular, methods cannot refer to instance
variables that are defined in supertypes.
It is possible to think of Oaklisp in terms of messages that are being
passed to objects, rather than in terms of operations that are being
performed on objects. The latter view was chosen because it is more
consistent with Lisp syntax and semantics.
\section{Fundamental Types}
There are two important relations in the Oaklisp type system: {\it
is-a} and \emph{subtype}. An object is related to its type by the
relation \emph{is-a}, and a type is related to its supertypes by the
relation \emph{subtype}. Each of these relations defines a tree
structure which includes all of the objects in the system.
The most fundamental types in the system are \df{type} and
\df{object}. They are distinguished by their position at the top of
the \emph{is-a} and \emph{subtype} hierarchies, and by their circular
definitions.
\ty{type}
\doc{This type is the top of the \emph{is-a} hierarchy. It is the
type of types, so new types are created by instantiating it.}
\ty{object}
\doc{This type is the top of the \emph{subtype}\ hierarchy, and has no
supertype. Every other type is a subtype of \df{object}, so default methods
for operations such as \df{print} are defined for \df{object}.}
\section{Operations on Objects}
The following operations are defined for all objects. Because they
determine the semantics of the language, they cannot be redefined or shadowed.
\op{get-type}{object}
\doc{Returns the type of \df{object}.}
\pr{eq?}{object object}
\doc{Determines object identity. Two objects may look and act the
same, but still fail the \df{eq?}\ test. In particular, numbers are
not guaranteed to be unique. Symbols \emph{are} interned, though.}
\section{Operations on Types}
Types are distinguished from other objects by the fact that they can
perform the \df{make} operation, which is the mechanism for generating
new objects.
\op{make}{type}
\doc{Returns a new instance of \df{type}.}
The instance variables of an object returned by \df{make} are
all bound to some unspecified value. Usually new objects need to be
initialized in some other way, which can be accomplished by performing
an operation on them immediately after they are made. By convention,
this operation is \df{initialize}.
\op{initialize}{object}
\doc{Returns \df{object}.}
This method for \df{initialize} is clearly a no-op. When a type
requires special initialization, it should shadow this default.
\section{Defining New Types}
Since types are objects, new ones are created by sending
a \df{make} message to the appropriate type object, which in this
case is \df{type}.
\makin{type}{ivars supertypes}
\doc{Returns a new type-object with the supertypes and instance
variables specified by the argument lists.}
At run-time, methods are chosen by performing a left-to-right
depth-first search on the supertype list.\footnote{Of course, Oaklisp
implementations are free to use more efficient mechanisms that have
the same effect.} Instances of the new type will contain a block of
instance variables for each of the ancestor types, although duplicate
types in the ancestor tree are eliminated.\footnote{This aspect of the
language is in flux, and should not be relied upon by users.}
\section{Type Predicates}
The implicit type checking performed by the method invocation
mechanism of Oaklisp reduces the need to call explicit type
predicates. Furthermore, the two predicates defined in this section
are sufficiently general to replace all of the ordinary Lisp type
predicates such as \df{null?} and \df{number?}. A few of these have
been retained to make the environment more familiar.
\pr{is-a?}{object type}
\doc{Determines whether \df{object} is an instance of \emph{type} or
one of its subtypes. \texttt{(is-a?\ \emph{object} object)} is always
true.}
\pr{subtype?}{type1 type2}
\doc{Determines whether \emph{type1} is a subtype of \emph{type2}.
As you would expect, \df{subtype?} is transitive. Since each type is
a subtype of itself, \df{subtype?} defines a partial ordering of all
the types in the system.}
\section{Constants}
Some objects have external representations that are not
self-evaluating expressions. \df{quote} allows the inclusion of such
objects as constants in code.
\sform{quote}{object}
\doc{Returns \emph{object} without evaluating it.}
\section{Standard Truth Values} \label{sec:truths}
The standard truth values of Oaklisp are represented by the objects
bound to the following variables.
\gv{t}
\doc{The value of this is \df{\#t}. Any non-false value will do just as
well for the purpose of logical tests.}
\gv{\#f}
\doc{This is the false value, the only object recognized by logical tests
as denoting falsehood.}
\gv{nil}
\doc{The value of this is the empty list, written \texttt{()}.
Notice that \df{nil} itself is just a variable, so \texttt{(eq?\ nil
'nil)} is false.}
\discuss{\textbf{Note:} currently \texttt{()} is the same as \texttt{\#f}, the
object used to represent falsehood. In the future it is possible that
these two notions, emptiness and falsehood, will be disconfabulated.
Programs should be written in such a way that if \df{\#f} and \df{()}
were not the same object, they would still work.}
\section{Coercion}
Some types are \emph{coercable}, meaning that there is an operation
associated with that type that allows objects to be coerced to that
type. To create a coercable type, one instantiates
\df{coercable-type} rather than \df{type}.
\lo{coercer}{coercable-type}
\doc{This returns the coercer of a type. For example, to coerce a
list into a string one uses \dfcoer{string}, as in \evto{((coercer
string) '(\#$\backslash$f \#$\backslash$o \#$\backslash$o))}{"foo"}. The reader
will read \texttt{frog} preceded by a control-y character as
\texttt{(coercer frog)}; this was motivated by the fact that control-y
prints as $\rightarrow$ on both Macintosh$^{\mbox{tm}}$ and Symbolics
computers, giving coercion a pleasant syntax,
\evto{($\rightarrow$string '(\#$\backslash$f \#$\backslash$o
\#$\backslash$o))}{"foo"}.}
\ty{coercable-type}
\doc{This is a subtype of \df{type} with has the added functionality
of responding to the \df{coercer} message by returning its coercion
operation. By default, \texttt{(is-a?\ \emph{foo} \emph{bar})}
implies that
\evto{((coercer \emph{bar}) \emph{foo})}{\emph{foo}}}
\section{Mixing Types}
Frequently, type hierarchies become so rich that they threaten to
overwhelm users with a plethora of possible combinations of mixins.
The combinatorial explosion of the number of possible concocted types
seems intrinsic to the style of programming involving multiple
functionally orthogonal mixins. Above a certain level of complexity,
finding a type with certain known characteristics can become
difficult. Programmers are left wondering ``Has a type based on
\emph{foo} with \emph{bar, baz} and \emph{zonk} mixed in been created, if so
what's its name, and if not what should I name it and where should I
define it?''
Oaklisp's \emph{mixin managers} take care of this problem. When one
needs ``the type based on \emph{foo} with \emph{bar, baz} and \emph{zonk}
mixed in,'' one asks a mixin manager for it. If such a type has
already been created, it is returned; if not, the mixin manager
creates an appropriate new type, caches it, and returns it. This
eliminates the burden of remembering which types have been concocted
and what they are named.
\op{mix-types}{mixin-manager type-list}
\doc{This returns a composite type whose supertypes are
\emph{type-list}. \emph{Mixin-manager} checks its cache, and if the
requested type is not found it creates a type with \texttt{(make type '()
\emph{type-list})}, caches it, and returns it.}
\ty{mixin-manager}
\doc{Instances of this cache composite types, acting as a sort of
composite type library.}
The Oaklisp operation type hierarchy is quite elaborite, containing a
large number of functionally orthogonal mixins, and therefore the
Oaklisp internals make heavy use of the mxin manager facility when
dealing with operations. For example, the following definition for
\df{+} is drawn from deep within the bowels of Oaklisp.
\begin{verbatim}
(define-constant-instance +
(mix-types oc-mixer
(list foldable-mixin open-coded-mixin operation)))
\end{verbatim}
|