File: types.tex

package info (click to toggle)
oaklisp 1.3.7-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 5,776 kB
  • sloc: ansic: 4,014; makefile: 149
file content (244 lines) | stat: -rw-r--r-- 10,178 bytes parent folder | download | duplicates (5)
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}