[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Functions are the basic building blocks of Lisp programs. This chapter describes the functions in Zetalisp that are used to manipulate functions. It also explains how to manipulate special forms and macros.
This chapter contains internal details intended for those writing programs to manipulate programs as well as material suitable for the beginner. Feel free to skip sections that look complicated or uninteresting when reading this for the first time.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are many different kinds of functions in Zetalisp. Here are the printed representations of examples of some of them:
foo (lambda (x) (car (last x))) (named-lambda foo (x) (car (last (x)))) (subst (x) (car (last x))) #<dtp-fef-pointer append 1424771> #<dtp-u-entry last 270> #<dtp-closure 1477464> |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The name of a function does not have to be a symbol. Various kinds of lists describe other places where a function can be found. A Lisp object which describes a place to find a function is called a function spec. ("Spec" is short for "specification".) Here are the printed representations of some typical function specs:
foo (:property foo bar) (:method tv:graphics-mixin :draw-line) (:internal foo 1) (:within foo bar) (:location #<dtp-locative 7435216>) |
Function specs have two purposes: they specify a place to remember a function, and they serve to name functions. The most common kind of function spec is a symbol, which specifies that the function cell of the symbol is the place to remember the function. We will see all the kinds of function spec, and what they mean, shortly. Function specs are not the same thing as functions. You cannot, in general, apply a function spec to arguments. The time to use a function spec is when you want to do something to the function, such as define it, look at its definition, or compile it.
Some kinds of functions remember their own names, and some don't. The "name" remembered by a function can be any kind of function spec, although it is usually a symbol. In the examples of functions in the previous section, the one starting with the symbol named-lambda, the one whose printed representation included dtp-fef-pointer, and the dtp-u-entry remembered names (the function specs foo, append, and last respectively). The others didn't remember their names.
To define a function spec means to make that function spec remember a given function. This is done with the fdefine function; you give fdefine a function spec and a function, and fdefine remembers the function in the place specified by the function spec. The function associated with a function spec is called the definition of the function spec. A single function can be the definition of more than one function spec at the same time, or of no function specs.
To define a function means to create a new function, and define a given function spec as that new function. This is what the defun special form does. Several other special forms, such as defmethod ((defmethod-fun)) and defselect ((defselect-fun)) do this too.
These special forms that define functions usually take a function spec, create a function whose name is that function spec, and then define that function spec to be the newly-created function. Most function definitions are done this way, and so usually if you go to a function spec and see what function is there, the function's name will be the same as the function spec. However, if you define a function named foo with defun, and then define the symbol bar to be this same function, the name of the function is unaffected; both foo and bar are defined to be the same function, and the name of that function is foo, not bar.
A function spec's definition in general consists of a basic definition surrounded by encapsulations. Both the basic definition and the encapsulations are functions, but of recognizably different kinds. What defun creates is a basic definition, and usually that is all there is. Encapsulations are made by function-altering functions such as trace and advise. When the function is called, the entire definition, which includes the tracing and advice, is used. If the function is "redefined" with defun, only the basic definition is changed; the encapsulations are left in place. See the section on encapsulations, (encapsulate).
A function spec is a Lisp object of one of the following types:
a symbol
(:property symbol property)
.setq method-function-spec section-page
(:method flavor-name message)
(:method flavor-name method-type message)
(:handler flavor-name message)
(:location pointer)
(:within within-function function-to-affect)
(:internal function-spec number)
Here is an example of the use of a function spec which is not a symbol:
(defun (:property foo bar-maker) (thing &optional kind) (set-the 'bar thing (make-bar 'foo thing kind))) |
(funcall (get 'foo 'bar-maker) 'baz) |
Unlike the other kinds of function spec, a symbol can be used as a function. If you apply a symbol to arguments, the symbol's function definition is used instead. If the definition of the first symbol is another symbol, the definition of the second symbol is used, and so on, any number of times. But this is an exception; in general, you can't apply function specs to arguments.
A keyword symbol which identifies function specs (may appear in the car of a list which is a function spec) is identified by a sys:function-spec-handler property whose value is a function which implements the various manipulations on function specs of that type. The interface to this function is internal and not documented in this manual.
For compatibility with Maclisp, the function-defining special forms defun, macro, and defselect (and other defining forms built out of them, such as defunp and defmacro) will also accept a list
(symbol property) |
(:property symbol property) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
(defun name lambda-list body...) |
defun creates a list which looks like
(named-lambda name lambda-list body...) |
Examples: (defun addone (x) (1+ x)) (defun foo (a &optional (b 5) c &rest e &aux j) (setq j (+ (addone a) b)) (cond ((not (null c)) (cons j e)) (t j))) |
addone is a function which expects a number as an argument, and returns a number one larger. foo is a complicated function which takes one required argument, two optional arguments, and any number of additional arguments which are given to the function as a list named e.
A declaration (a list starting with declare) can appear as the first element of the body. It is equivalent to a local-declare (see (local-declare-fun)) surrounding the entire defun form. For example,
(defun foo (x) (declare (special x)) (bar)) ;bar uses x free. |
(local-declare ((special x)) (defun foo (x) (bar))) |
A documentation string can also appear as the first element of the body (following the declaration, if there is one). (It shouldn't be the only thing in the body; otherwise it is the value returned by the function and so is not interpreted as documentation. A string as an element of a body other than the last element is only evaluated for side-effect, and since evaluation of strings has no side effects, they aren't useful in this position to do any computation, so they are interpreted as documentation.) This documentation string becomes part of the function's debugging info and can be obtained with the function documentation (see (documentation-fun)). The first line of the string should be a complete sentence which makes sense read by itself, since there are two editor commands to get at the documentation, one of which is "brief" and prints only the first line. Example:
(defun my-append (&rest lists) "Like append but copies all the lists. This is like the Lisp function append, except that append copies all lists except the last, whereas this function copies all of its arguments including the last one." ...) |
(defunp fctn (args) form1 form2 ... formn) |
(defun fctn (args) (prog () form1 form2 ... (return formn))) |
You can think of defunp as being like defun except that you can return out of the middle of the function's body.
For more information on defining functions, and other ways of doing so, see (function-defining).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here is a list of the various things a user (as opposed to a program) is likely to want to do to a function. In all cases, you specify a function spec to say where to find the function.
To print out the definition of the function spec with indentation to make it legible, use grindef (see (grindef-fun)). This works only for interpreted functions. If the definition is a compiled function, it can't be printed out as Lisp code, but its compiled code can be printed by the disassemble function (see (disassemble-fun)).
To find out about how to call the function, you can ask to see its documentation, or its argument names. (The argument names are usually chosen to have mnemonic significance for the caller). Use arglist ((arglist-fun)) to see the argument names and documentation ((documentation-fun)) to see the documentation string. There are also editor commands for doing these things: the CTRL/SHIFT/D and META/SHIFT/D commands are for looking at a function's documentation, and CTRL/SHIFT/A is for looking at an argument list. CTRL/SHIFT/A does not ask for the function name; it acts on the function which is called by the innermost expression which the cursor is inside. Usually this is the function which will be called by the form you are in the process of writing.
You can see the function's debugging info alist by means of the function debugging-info (see (debugging-info-fun)).
When you are debugging, you can use trace (see (trace-fun)) to obtain a printout or a break loop whenever the function is called. You can use breakon (see (breakon-fun)) to cause the error handler to be entered whenever the function is called; from there, you can step through further function calls and returns. You can customize the definition of the function, either temporarily or permanently, using advise (see (advise-fun)).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are many kinds of functions in Zetalisp. This section briefly describes each kind of function. Note that a function is also a piece of data and can be passed as an argument, returned, put in a list, and so forth.
Before we start classifying the functions, we'll first discuss something about how the evaluator works. As we said in the basic description of evaluation on (description-of-evaluation), when the evaluator is given a list whose first element is a symbol, the form may be a function form, a special form, or a macro form. If the definition of the symbol is a function, then the function is just applied to the result of evaluating the rest of the subforms. If the definition is a cons whose car is macro, then it is a macro form; these are explained in (macro). What about special forms?
.setq special-function page Conceptually, the evaluator knows specially about all special forms (that's why they're called that). However, the Zetalisp implementation actually uses the definition of symbols that name special forms as places to hold pieces of the evaluator. The definitions of such symbols as prog, do, and, and or actually hold Lisp objects, which we will call special functions. Each of these functions is the part of the Lisp interpreter that knows how to deal with that special form. Normally you don't have to know about this; it's just part of the hidden internals of how the evaluator works. However, if you try to add encapsulations to and or something like that, knowing this will help you understand the behavior you will get.
Special functions are written like regular functions except that the keywords "e and &eval (see (lambda-list-keywords)) are used to make some of the arguments be "quoted" arguments. The evaluator looks at the pattern in which arguments to the special function are "quoted" or not, and it calls the special function in a special way: for each regular argument, it passes the result of evaluating the corresponding subform, but for each "quoted" argument, it passes the subform itself without evaluating it first. For example, cond works by having a special function that takes a "quoted" &rest argument; when this function is called it is passed a list of cond clauses as its argument.
If you apply or funcall a special function yourself, you have to understand what the special form is going to do with its arguments; it is likely to call eval on parts of them. This is different from applying a regular function, which is passed argument values rather than Lisp expressions.
.setq special-form-caveat page
Defining your own special form, by using "e yourself, can be done; it is a way to extend the Lisp language. Macros are another way of extending the Lisp language. It is preferable to implement language extensions as macros rather than special forms, because macros directly define a Lisp-to-Lisp translation and therefore can be understood by both the interpreter and the compiler. Special forms, on the other hand, only extend the interpreter. The compiler has to be modified in an ad hoc way to understand each new special form so that code using it can be compiled. Since all real programs are eventually compiled, writing your own special functions is strongly discouraged.
(In fact, many of the special forms in Zetalisp are actually implemented as macros, rather than as special functions. They're implemented this way because it's easier to write a macro than to write both a new special function and a new ad hoc module in the compiler. However, they're sometimes documented in this manual as special forms, rather than macros, because you should not in any way depend on the way they are implemented; they might get changed in the future to be special functions, if there was some reason to do so.)
There are four kinds of functions, classified by how they work.
First, there are interpreted functions: you define them with defun, they are represented as list structure, and they are interpreted by the Lisp evaluator.
Secondly, there are compiled functions: they are defined by compile or by loading a qfasl file, they are represented by a special Lisp data type, and they are executed directly by the microcode. Similar to compiled functions are microcode functions, which are written in microcode (either by hand or by the micro-compiler) and executed directly by the hardware.
Thirdly, there are various types of Lisp object which can be applied to arguments, but when they are applied they dig up another function somewhere and apply it instead. These include dtp-select-method, closures, instances, and entities.
Finally, there are various types of Lisp object which, when used as functions, do something special related to the specific data type. These include arrays and stack-groups.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
An interpreted function is a piece of list structure which represents a program according to the rules of the Lisp interpreter. Unlike other kinds of functions, an interpreted function can be printed out and read back in (it has a printed representation that the reader understands), can be pretty-printed (see (grindef-fun)), and can be opened up and examined with the usual functions for list-structure manipulation.
There are four kinds of interpreted functions: lambdas, named-lambdas, substs, and named-substs. A lambda function is the simplest kind. It is a list that looks like this:
(lambda lambda-list form1 form2...) |
A named-lambda is like a lambda but contains an extra element in which the system remembers the function's name, documentation, and other information. Having the function's name there allows the error handler and other tools to give the user more information. This is the kind of function that defun creates. A named-lambda function looks like this:
(named-lambda name lambda-list body forms...) |
(defun (foo bar) (x) (car (reverse x))) |
(named-lambda ((:property foo bar)) (x) (car (reverse x))) |
.setq subst section-page A subst is just like a lambda as far as the interpreter is concerned. It is a list that looks like this:
(subst lambda-list form1 form2...) |
A named-subst is the same as a subst except that it has a name just as a named-lambda does. It looks like
(named-subst name lambda-list form1 form2 ...) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are two kinds of compiled functions: macrocoded functions and microcoded functions. The Lisp compiler converts lambda and named-lambda functions into macrocoded functions. A macrocoded function's printed representation looks like:
#<dtp-fef-pointer append 1424771> |
The printed representation of a microcoded function looks like:
#<dtp-u-entry last 270> |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A closure is a kind of function which contains another function and a set of special variable bindings. When the closure is applied, it puts the bindings into effect and then applies the other function. When that returns, the closure bindings are removed. Closures are made with the function closure. See (closure) for more information. Entities are slightly different from closures; see (entity).
.setq select-method page A select-method (dtp-select-method) is an a-list of symbols and functions. When one is called the first argument is looked up in the a-list to find the particular function to be called. This function is applied to the rest of the arguments. The a-list may have a list of symbols in place of a symbol, in which case the associated function is called if the first argument is any of the symbols on the list. If cdr of last of the a-list is not nil, it is a default handler function, which gets called if the message key is not found in the a-list. Select-methods can be created with the defselect special form (see (defselect-fun)).
An instance is a message-receiving object which has some state and a table of message-handling functions (called methods). Refer to the chapter on flavors ((flavor)) for further information.
An array can be used as a function. The arguments to the array are the indices and the value is the contents of the element of the array. This works this way for Maclisp compatibility and is not recommended usage. Use aref ((aref-fun)) instead.
A stack group can be called as a function. This is one way to pass control to another stack group. See (stack-group).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
defun is a special form which is put in a program to define a function. defsubst and macro are others. This section explains how these special forms work, how they relate to the different kinds of functions, and how they interface to the rest of the function-manipulation system.
Function-defining special forms typically take as arguments a function spec and a description of the function to be made, usually in the form of a list of argument names and some forms which constitute the body of the function. They construct a function, give it the function spec as its name, and define the function spec to be the new function. Different special forms make different kinds of functions. defun makes a named-lambda function, and defsubst makes a named-subst function. macro makes a macro; though the macro definition is not really a function, it is like a function as far as definition handling is concerned.
These special forms are used in writing programs because the function names and bodies are constants. Programs that define functions usually want to compute the functions and their names, so they use fdefine. See (fdefine-fun).
All of these function-defining special forms alter only the basic definition of the function spec. Encapsulations are preserved. See (encapsulate).
The special forms only create interpreted functions. There is no special way of defining a compiled function. Compiled functions are made by compiling interpreted ones. The same special form which defines the interpreted function, when processed by the compiler, yields the compiled function. See (compiler) for details.
Note that the editor understands these and other "defining" special forms (e.g. defmethod, defvar, defmacro, defstruct, etc.) to some extent, so that when you ask for the definition of something, the editor can find it in its source file and show it to you. The general convention is that anything which is used at top level (not inside a function) and starts with def should be a special form for defining things and should be understood by the editor. defprop is an exception.
.setq additional-defun-explanation page The defun special form (and the defunp macro which expands into a defun) are used for creating ordinary interpreted functions (see (defun-fun)).
For Maclisp compatibility, a type symbol may be inserted between name and lambda-list in the defun form. The following types are understood:
expr
fexpr
macro
If lambda-list is a non-nil symbol instead of a list, the function is recognized as a Maclisp lexpr and it is converted in such a way that the arg, setarg, and listify functions can be used to access its arguments (see (arg-fun)).
The defsubst special form is used to create substitutible functions. It is used just like defun but produces a list starting with named-subst instead of one starting with named-lambda. The named-subst function acts just like the corresponding named-lambda function when applied, but it can also be open-coded (incorporated into its callers) by the compiler. See (defsubst-fun) for full information.
The macro special form is the primitive means of creating a macro. It gives a function spec a definition which is a macro definition rather than a actual function. A macro is not a function because it cannot be applied, but it can appear as the car of a form to be evaluated. Most macros are created with the more powerful defmacro special form. See (macro).
The defselect special form defines a select-method function. See (defselect-fun).
Unlike the above special forms, the next two (deff and def) do not create new functions. They simply serve as hints to the editor that a function is being stored into a function spec here, and therefore if someone asks for the source code of the definition of that function spec, this is the place to look for it.
(def function-spec form1 form2...) |
Alternatively, you could put (def function-spec) in front of or anywhere near the forms which define the function. The editor only uses it to tell which line to put the cursor on.
(deff foo 'bar) |
(deff foo (function bar)) |
The special form looks like
(defselect (function-spec default-handler no-which-operations) (message-name (args...) body...) (message-name (args...) body...) ...) |
function-spec is the name of the function to be defined. default-handler is optional; it must be a symbol and is a function which gets called if the select-method is called with an unknown message. If default-handler is unsupplied or nil, then an error occurs if an unknown message is sent. If no-which-operations is non-nil, the :which-operations method which would normally be supplied automatically is suppressed. The :which-operations method takes no arguments and returns a list of all the message names in the defselect.
If function-spec is a symbol, and default-handler and no-which-operations are not supplied, then the first subform of the defselect may be just function-spec by itself, not enclosed in a list.
The remaining subforms in a defselect define methods. message-name is the message name, or a list of several message names if several messages are to be handled by the same subfunction. args is a lambda-list; it should not include the first argument, which is the message name. body is the body of the function.
A method subform can instead look like:
(message-name . symbol) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section documents all the keywords that may appear in the "lambda-list" (argument list) (see (lambda-list)) of a function, a macro, or a special form. Some of them are allowed everywhere, while others are only allowed in one of these contexts; those are so indicated.
&optional
&rest
&key
&allow-other-keys
&aux
(variable initial-value-form) |
&special
&local
&functional
"e
&eval
&list-of
&body
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
.setq programs-that-manipulate-functions section-page
If fdefine is called while a file is being loaded, it records what file the function definition came from so that the editor can find the source code.
If function-spec was already defined as a function, and carefully is non-nil, the function-spec's :previous-definition property is used to save the previous definition. If the previous definition is an interpreted function, it is also saved on the :previous-expr-definition property. These properties are used by the undefun function ((undefun-fun)), which restores the previous definition, and the uncompile function ((uncompile-fun)), which restores the previous interpreted definition. The properties for different kinds of function specs are stored in different places; when a function spec is a symbol its properties are stored on the symbol's property list.
defun and the other function-defining special forms all supply t for carefully and nil or nothing for no-query. Operations which construct encapsulations, such as trace, are the only ones which use nil for carefully.
(fdefine symbol definition t force-flag) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
These functions take a function as argument and return information about that function. Some also accept a function spec and operate on its definition. The others do not accept function specs in general but do accept a symbol as standing for its definition. (Note that a symbol is a function as well as a function spec).
(defun foo (&rest rest-arg) (declare (arglist x y &rest z)) .....) |
arglist cannot be relied upon to return the exactly correct answer, since some of the information may have been lost. Programs interested in how many and what kind of arguments there are should use args-info instead. In general arglist is to be used for documentation purposes, not for reconstructing the original source code of the function. When a function returns multiple values, it is useful to give the values names so that the caller can be reminded which value is which. By means of a return-list declaration in the function's definition, entirely analogous to the arglist declaration above, you can specify a list of mnemonic names for the returned values. This list will be returned by arglist as the second value.
(arglist 'arglist) => (function &optional real-flag) and (arglist return-list) |
The information is stored in various bits and byte fields in the fixnum, which are referenced by the symbolic names shown below. By the usual Lisp Machine convention, those starting with a single "%" are bit-masks (meant to be logand'ed or bit-test'ed with the number), and those starting with "%%" are byte descriptors (meant to be used with ldb or ldb-test). Here are the fields:
%%arg-desc-min-args
%%arg-desc-max-args
%arg-desc-evaled-rest
%arg-desc-quoted-rest
%arg-desc-fef-quote-hair
%arg-desc-interpreted
%arg-desc-fef-bind-hair
Note that %arg-desc-quoted-rest and %arg-desc-evaled-rest cannot both be set.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The definition of a function spec actually has two parts: the basic definition, and encapsulations. The basic definition is what functions like defun create, and encapsulations are additions made by trace or advise to the basic definition. The purpose of making the encapsulation a separate object is to keep track of what was made by defun and what was made by trace. If defun is done a second time, it replaces the old basic definition with a new one while leaving the encapsulations alone.
Only advanced users should ever need to use encapsulations directly via the primitives explained in this section. The most common things to do with encapsulations are provided as higher-level, easier-to-use features: trace (see (trace-fun)) and advise (see (advise-fun)).
The way the basic definition and the encapsulations are defined is that the actual definition of the function spec is the outermost encapsulation; this contains the next encapsulation, and so on. The innermost encapsulation contains the basic definition. The way this containing is done is as follows. An encapsulation is actually a function whose debugging info alist contains an element of the form
(si:encapsulated-definition uninterned-symbol encapsulation-type) |
uninterned-symbol's function definition is the thing that the encapsulation contains, usually the basic definition of the function spec. Or it can be another encapsulation, which has in it another debugging info item containing another uninterned symbol. Eventually you get to a function which is not an encapsulation; it does not have the sort of debugging info item which encapsulations all have. That function is the basic definition of the function spec.
Literally speaking, the definition of the function spec is the outermost encapsulation, period. The basic definition is not the definition. If you are asking for the definition of the function spec because you want to apply it, the outermost encapsulation is exactly what you want. But the basic definition can be found mechanically from the definition, by following the debugging info alists. So it makes sense to think of it as a part of the definition. In regard to the function-defining special forms such as defun, it is convenient to think of the encapsulations as connecting between the function spec and its basic definition.
An encapsulation is created with the macro si:encapsulate.
(si:encapsulate function-spec outer-function type body-form extra-debugging-info) |
function-spec evaluates to the function spec whose definition the new encapsulation should become. outer-function is another function spec, which should often be the same one. Its only purpose is to be used in any error messages from si:encapsulate.
type evaluates to a symbol which identifies the purpose of the encapsulation; it says what the application is. For example, it could be advise or trace. The list of possible types is defined by the system because encapsulations are supposed to be kept in an order according to their type (see si:encapsulation-standard-order, (si:encapsulation-standard-order-var)). type should have an si:encapsulation-grind-function property which tells grindef what to do with an encapsulation of this type.
body-form is a form which evaluates to the body of the encapsulation-definition, the code to be executed when it is called. Backquote is typically used for this expression; see (backquote). si:encapsulate is a macro because, while body is being evaluated, the variable si:encapsulated-function is bound to a list of the form (function uninterned-symbol), referring to the uninterned symbol used to hold the prior definition of function-spec. If si:encapsulate were a function, body-form would just get evaluated normally by the evaluator before si:encapsulate ever got invoked, and so there would be no opportunity to bind si:encapsulated-function. The form body-form should contain (apply ,si:encapsulated-function arglist) somewhere if the encapsulation is to live up to its name and truly serve to encapsulate the original definition. (The variable arglist is bound by some of the code which the si:encapsulate macro produces automatically. When the body of the encapsulation is run arglist's value will be the list of the arguments which the encapsulation received.)
extra-debugging-info evaluates to a list of extra items to put into the debugging info alist of the encapsulation function (besides the one starting with si:encapsulated-definition which every encapsulation must have). Some applications find this useful for recording information about the encapsulation for their own later use.
When a special function is encapsulated, the encapsulation is itself a special function with the same argument quoting pattern. (Not all quoting patterns can be handled; if a particular special form's quoting pattern cannot be handled, si:encapsulate signals an error.) Therefore, when the outermost encapsulation is started, each argument has been evaluated or not as appropriate. Because each encapsulation calls the prior definition with apply, no further evaluation takes place, and the basic definition of the special form also finds the arguments evaluated or not as appropriate. The basic definition may call eval on some of these arguments or parts of them; the encapsulations should not.
Macros cannot be encapsulated, but their expander functions can be; if the definition of function-spec is a macro, then si:encapsulate automatically encapsulates the expander function instead. In this case, the definition of the uninterned symbol is the original macro definition, not just the original expander function. It would not work for the encapsulation to apply the macro definition. So during the evaluation of body-form, si:encapsulated-function is bound to the form (cdr (function uninterned-symbol)), which extracts the expander function from the prior definition of the macro.
Because only the expander function is actually encapsulated, the encapsulation does not see the evaluation or compilation of the expansion itself. The value returned by the encapsulation is the expansion of the macro call, not the value computed by the expansion.
It is possible for one function to have multiple encapsulations, created by different subsystems. In this case, the order of encapsulations is independent of the order in which they were made. It depends instead on their types. All possible encapsulation types have a total order and a new encapsulation is put in the right place among the existing encapsulations according to its type and their types.
(advise breakon trace si:rename-within) |
Every symbol used as an encapsulation type must be on the list si:encapsulation-standard-order. In addition, it should have an si:encapsulation-grind-function property whose value is a function that grindef will call to process encapsulations of that type. This function need not take care of printing the encapsulated function because grindef will do that itself. But it should print any information about the encapsulation itself which the user ought to see. Refer to the code for the grind function for advise to see how to write one.
To find the right place in the ordering to insert a new encapsulation, it is necessary to parse existing ones. This is done with the function si:unencapsulate-function-spec.
If the definition of function-spec is an encapsulation, then its debugging info is examined to find the uninterned symbol which holds the encapsulated definition, and also the encapsulation type. If the encapsulation is of a type which is to be skipped over, the uninterned symbol replaces the original function spec and the process repeats.
The value returned is the uninterned symbol from inside the last encapsulation skipped. This uninterned symbol is the first one which does not have a definition which is an encapsulation that should be skipped. Or the value can be function-spec if function-spec's definition is not an encapsulation which should be skipped.
The types of encapsulations to be skipped over are specified by encapsulation-types. This can be a list of the types to be skipped, or nil meaning skip all encapsulations (this is the default). Skipping all encapsulations means returning the uninterned symbol which holds the basic definition of function-spec. That is, the definition of the function spec returned is the basic definition of the function spec supplied. Thus,
(fdefinition (si:unencapsulate-function-spec 'foo)) |
(fdefine (si:unencapsulate-function-spec 'foo) 'bar) |
encapsulation-types can also be a symbol, which should be an encapsulation type; then we skip all types which are supposed to come outside of the specified type. For example, if encapsulation-types is trace, then we skip all types of encapsulations that come outside of trace encapsulations, but we do not skip trace encapsulations themselves. The result is a function spec which is where the trace encapsulation ought to be, if there is one. Either the definition of this function spec is a trace encapsulation, or there is no trace encapsulation anywhere in the definition of function-spec, and this function spec is where it would belong if there were one. For example,
(let ((tem (si:unencapsulate-function-spec spec 'trace))) (and (eq tem (si:unencapsulate-function-spec tem '(trace))) (si:encapsulate tem spec 'trace `(...body...)))) |
(let ((tem (si:unencapsulate-function-spec spec 'trace))) (fdefine tem (fdefinition (si:unencapsulate-function-spec tem '(trace))))) |
These examples show how a subsystem can insert its own type of encapsulation in the proper sequence without knowing the names of any other types of encapsulations. Only the variable si:encapsulation-standard-order, which is used by si:unencapsulate-function-spec, knows the order.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
One special kind of encapsulation is the type si:rename-within. This encapsulation goes around a definition in which renamings of functions have been done.
How is this used?
If you define, advise, or trace (:within foo bar), then bar gets renamed to altered-bar-within-foo wherever it is called from foo, and foo gets a si:rename-within encapsulation to record the fact. The purpose of the encapsulation is to enable various parts of the system to do what seems natural to the user. For example, grindef (see (grindef-fun)) notices the encapsulation, and so knows to print bar instead of altered-bar-within-foo, when grinding the definition of foo.
Also, if you redefine foo, or trace or advise it, the new definition gets the same renaming done (bar replaced by altered-bar-within-foo). To make this work, everyone who alters part of a function definition should pass the new part of the definition through the function si:rename-within-new-definition-maybe.
It is not necessary to call this function yourself when you replace the basic definition because fdefine with carefully supplied as t does it for you. si:encapsulate does this to the body of the new encapsulation. So you only need to call si:rename-within-new-definition-maybe yourself if you are rplac'ing part of the definition.
For proper results, function-spec must be the outer-level function spec. That is, the value returned by si:unencapsulate-function-spec is not the right thing to use. It will have had one or more encapsulations stripped off, including the si:rename-within encapsulation if any, and so no renamings will be done.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |