File: methods.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 (181 lines) | stat: -rw-r--r-- 8,709 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
% 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{Methods and Scoping} \label{methods}

In Chapter~\ref{types}, the concept of \emph{type} was discussed.
The assertion was made that operation methods lie at the heart of the
typing system, because they determine the behavior of objects.  This
chapter describes the mechanism for defining methods.

\section{Methods}

A table of methods is maintained in the descriptor of every type. At
run-time, these tables are searched to find the methods which are used
to handle operations on objects. The only mechanism for manipulating
method tables is the following side-effecting special form.

\sform{add-method}{\lpar operation $[$\lpar type \dt
ivar-list\rpar$]$ \dt arg-list\rpar \dt body}
\doc{Adds a method for \emph{operation} to the method table of \emph{type}.
If a method for \emph{operation} already exists, it is replaced.  The
value returned by \df{add-method} is \emph{operation}.}

The body of the form is surrounded by an implicit \df{block}.  The
arguments to the method are specified by \emph{arg-list}.  Since the
first argument is always the object handling the message, a useful
convention is to call it \df{self}.  Instance variables of \emph{type}
can be referenced in the body if they are declared in \emph{ivar-list}.
Instance variables of supertypes may not be referenced in any case.
Naming conflicts between instance variables and arguments are resolved
by the rule that the variables in \emph{arg-list} shadow instance
variables that have the same names.  Oaklisp closes methods over free
variable references at compile-time, thereby solving the upward funarg
problem and allowing procedures to share state in a controlled manner.


\section{Scoping}

Oaklisp is a lexically scoped language in which all variable
references are resolved at compile-time.  When a variable reference is
encountered, the compiler searches outwards from that point through
the nested lexical binding contours until it finds a declaration for
the variable.\footnote{If a declaration isn't found, the compiler
proceeds to look for the variable in the appropriate locale. See
Chapter~\ref{locales}.} We have already seen one mechanism for
introducing new lexical contours: the argument list of the
\df{add-method} special form.  Oaklisp provides several other forms
which can be used to define local variables and procedures.

\sform{let}
{\lpar\lpar var$_1$ val$_1$\rpar\ldots var$_n$ val$_n$\rpar \dt body}
\doc{Evaluates \emph{body} in an environment where the $n$ variables
are bound to the $n$ values. The value returned is that of
\emph{body}.}

\sform{let*}
{\lpar\lpar var$_1$ val$_1$\rpar\ldots var$_n$ val$_n$\rpar \dt body}
\doc{This form is similar to \df{let}.  The difference is that \df{let}
performs the bindings simultaneously whereas \df{let*} performs the bindings
sequentially so that each value expression can refer to the preceding
variables.}

\sform{labels}
{\lpar\lpar var$_1$ val$_1$\rpar\ldots\lpar var$_n$ val$_n$\rpar\rpar \dt body}
\doc{\df{labels} differs from \df{let} in that the value expressions are
evaluated in a binding environment in which all of the variables are already
defined.  This facilitates the definition of mutually recursive procedures.}


\section{Functional Syntax}

Sometimes it is convenient to adopt a more conventional Lisp
viewpoint while designing programs.  This viewpoint considers functions
to be the primary programming abstraction, with objects downgraded to
the status of data which is passed around between functions. The key
to this programming style is the ability to write functions which can accept
arguments of any type.

Oaklisp readily accommodates the functional programming style, since
methods can be defined for the type \df{object}, which is the
supertype of all other types.  In fact, if the type specifier is
omitted in an \df{add-method} form, the type \df{object} is assumed.
Thus, \texttt{(add-method (\mbox{cons-1} x) (cons x 1))} defines a method that
is valid for any type.  To give the language a more familiar
appearance when this programming style is used, the following macros
are also provided.

\mc{lambda}{arg-list \dt body}
\doc{\macdef{}{(add-method ((make operation) . \emph{arg-list}) . \emph{body})}}

\mc{define}{\lpar variable \dt arg-list\rpar \dt body}
\doc{\macdef{}{(define \emph{variable} (lambda \emph{arg-list} . \emph{body}))}}


\section{Dispatching to Supertypes}

Sometimes a method doesn't want to override the inherited method
completely, but rather wishes only to modify or extend its
behaviour.  For instance, imagine that the type \texttt{dog} has a method
so that the \texttt{notice-stranger} operation causes it to run around,
jump up and down, bark, and return the amount of time wasted.  Say
that \texttt{stupid-dog} is a subtype of \texttt{dog} defined by
\texttt{(define-instance stupid-dog type '() (list dog))}, and that we
want stupid dogs to behave just like regular dogs in response to a
\texttt{see-stranger} message, except that they do it twice.  This could
be accomplished without the duplication of code by dispatching to the
supertype twice, as in the following code fragment.
\begin{flushleft}\tt
(add-method (see-stranger (stupid-dog) self stranger)\\
~~(+ ({\upar}super dog see-stranger self stranger)\\
~~~~~({\upar}super dog see-stranger self stranger)))
\end{flushleft}


\op{{\protect\upar}super}{type operation self \dt args}
\doc{This is just like \texttt{(\emph{operation self \dt args})} except
that the method search begins at \emph{type} rather than at the type of
\emph{self}.  It is required that \emph{type} be an immediate supertype
of the type that the method this call appears in is added to, although
our current implementation does not yet enforce this restriction.
\df{{\protect\upar}super} is analogous to the Smalltalk-80
\index{Smalltalk-80} mechanism of the same name, except that due to
Oaklisp's multiple inheritance it is necessary for the programmer to
explicitly state which supertype is to be dispatched to.}



\section{Rest Args}

When a method is defined with a parameter list that is improper (\ie\
dotted) the method is permitted to receive extra values in addition to
its regular parameters at run time.  These values are associated with
the pseudo variable name that appears after the dot, which will
henceforth be called the rest name.  Unlike a real variable name, a
rest name can't be evaluated and can only be referred to in two
places: at the end of a function call that uses dotted syntax (which
signifies that the extra values should be passed on to the function
being called), and in a \df{rest-length} form, which is the mechanism
for finding out how many rest args a method has been passed.

\sform{rest-length}{rest-name}
\doc{Yields the number of extra values that were received by the
method in which \df{rest-name} is declared.}

Rest args can never be accessed directly, but must be passed tail
recursively to other functions.  In fact, a function is not permitted
to return without disposing of its rest args.  Usually a function that
takes a variable number of arguments will recurse on itself or on a
helper function, consuming its arguments one by one until they are all
gone, at which point the function is free to return.

The following functions have been provided to make it easier to write a
function definition that satisfies all of the rules for rest args.

\op{consume-args}{val \dt args}
\doc{Returns \emph{val} after consuming \emph{args}.}

\op{listify-args}{op \dt args}
\doc{Calls \emph{op} on a list consisting of the values of \emph{args}.}

A call to \df{listify-args} can be used as the body of a
method definition as a means of trivially satisfying the rest arg
rules.  When using this technique, \emph{op} is a lambda that performs
all of the computation for the method.  The rest args of the method
are wrapped up in a list that is passed in as the lambda's one
parameter, and the regular parameters and instance variables of the
method are available inside the lambda because of lexical scoping.