| Allegro CL version 9.0 Moderately revised from 8.2. 8.2 version |
This document contains the following sections:
1.0 Implementation introductionThe Common Lisp standard is deliberately vague on many of the specifics of an implementation. The authors of that book were aware that implementation details are dependent on the nature of the hardware and the operating system, as well as the differing priorities of the implementors and the different user communities. This document details some of the specifics of the implementation of and extensions in Allegro CL.
Allegro CL contains all of the required Common Lisp data types. Fixnums are signed 30-bit quantities (29 bits of value, one sign bit) on 32-bit machines and signed 61-bit quantities (60 bits of value, one sign bit) on 64 bit machines. There are two distinct floating-point types on all platforms (32 bit and 64 bit floats). Short-float and single-float are equivalent and are 32 bit floats. Double-float and long-float are equivalent and are 64 bit floats.
The distinct array data types are shown in the following list (in the
case of the simple arrays, we use suspension points, `...', to
indicate that there may be any number of dimensions). When you specify
an element-type to make-array, you will get an array whose element
type is upgraded-array-element-type applied to the value
specified. Arrays of type t
are general
arrays and arrays of any other type are called specialized
arrays. After the list, we give some examples of the types of arrays
created with particular values for element-type.
The new short-array type is not mentioned in this list. See Section 3.0 Arrays and short arrays for information on short-arrays and also on maximum array sizes.
Allegro CL allows most types of arrays to be allocated in static space
(where they are never moved or even looked at by the garbage
collector). See Section 6.10 cl:make-array for
information on creating such arrays. Only (as noted) arrays of type
t
cannot be allocated in static space
(because such arrays usually contain pointers to other Lisp objects
whicxh must be looked at and updated by the garbage collector when the
objects pointed to are relocated).
(array t) ;; cannot have :allocation :static, ;; :malloc, or :static-reclaimable as noted above (array bit) (array (unsigned-byte 4)) (array (unsigned-byte 8)) (array (unsigned-byte 16)) (array (unsigned-byte 32)) (array (unsigned-byte 64)) [64-bit Lisps only] (array character) (array single-float) (array double-float) (array fixnum) (array (complex single-float)) (array (complex double-float)) (array (signed-byte 8)) (array (signed-byte 16)) (array (signed-byte 32)) (array (signed-byte 64)) [64-bit Lisps only] (array nil) (simple-array t (* ...)) (simple-array bit (* ...)) (simple-array (unsigned-byte 4) (* ...)) (simple-array (unsigned-byte 8) (* ...)) (simple-array (unsigned-byte 16) (* ...)) (simple-array (unsigned-byte 32) (* ...)) (simple-array (unsigned-byte 64) (* ...)) [64-bit Lisps only] (simple-array character (* ...)) (simple-array single-float (* ...)) (simple-array double-float (* ...)) (simple-array fixnum (* ...)) (simple-array (signed-byte 8) (* ...)) (simple-array (signed-byte 16) (* ...)) (simple-array (signed-byte 32) (* ...)) (simple-array (signed-byte 64) (* ...)) [64-bit Lisps only] (simple-array nil (* ...))
Now let us look at some examples. When we specify
(unsigned-byte 3)
as the value of
element-type, we get an array of type
(unsigned-byte 4)
:
cl-user(2): (setq fn-arr (make-array 5 :element-type '(unsigned-byte 3) :initial-element 0)) #(0 0 0 0 0) cl-user(3): (array-element-type fn-arr) (unsigned-byte 4) cl-user(4): (upgraded-array-element-type '(unsigned-byte 3)) (unsigned-byte 4) cl-user(5):
Note that upgraded-array-element-type applied to
(unsigned-byte 3)
returns (unsigned-byte
4)
. Note too that we have specified 0 as the value of the
initial-value. If we had not, the initial value
would be nil
, which is not of type
(unsigned-byte 4)
.
Here is what is returned by upgraded-array-element-type for some other common types:
cl-user(7): (upgraded-array-element-type 'single-float) single-float cl-user(8): (upgraded-array-element-type 'double-float) double-float cl-user(9): (upgraded-array-element-type 'float) t cl-user(10): (upgraded-array-element-type 'integer) t cl-user(11): (upgraded-array-element-type 'character) character cl-user(12): (upgraded-array-element-type '(signed-byte 6)) (signed-byte 8) cl-user(13): (upgraded-array-element-type '(unsigned-byte 100)) t
Note that specifying float
and
integer
both result in arrays of type t
, not in specialized arrays. Specifying signed or
unsigned bytes of particular sizes results in that size or bigger, or
possibly t
.
It is good programming practice to use upgraded-array-element-type to determine exactly what sort of array you will get.
Certain types of vectors can be stack allocated, thus saving space in applications. See Stack consing, avoiding consing using apply, and stack allocation in compiling.htm for details.
Arrays are stored internally as vectors. The underlying vector associated with an array is accessible with the macro excl:with-underlying-simple-vector.
Release 7.0 contains a new array implementation with larger array size
limits: now most-positive-fixnum
. In earlier releases, the
limit in 32-bit Lisps was (expt 2 24)
, a value 32
times smaller. In 64-bit images, the limit was (expt 2
56)
, 16 times smaller than the new most-positive-fixnum
limit.
Because the structure of arrays had to change in order to implement this change, and because there exists the possibility that users have done some coding which assumes a particular arrangement for arrays (such as is the case for the lisp.h file for compiling C code to recognize lisp structure), we have retained the older array types with their smaller limits, and have renamed them to be short arrays.
The make-array function now
accepts the additional :short keyword
argument. :short defaults to nil
and when nil
, a (long)
array is produced, and when specified true, a short array (that used
in earlier releases) is produced, with these exceptions:
nil
a (long) array is produced, regardless of
the value of :short.
excl::foreign
, a short array is produced,
regardless of the value of :short.
The next section discusses these and other anomalies.
The new functions short-vector and short-string create short vectors and short strings, analogously to the standard functions vector and string.
Most of the array set is symmetrical with respect to short-ness; i.e. a call to make-array for most element types will either produce a simple-array or short-simple-array of the specified element-type, based on the :short argument, (and, for specifications which normally create non-simple arrays, these results will be either arrays or short-arrays of the specified element-type) with the following exceptions:
(unsigned-byte 32)
in 32-bit images and
(unsigned-byte 64)
in 64-bit images. For example:
CL-USER(1): (featurep :64bit) NIL CL-USER(2): (type-of (make-array 10 :element-type 'fixnum)) (SIMPLE-ARRAY FIXNUM (10)) CL-USER(3): (type-of (make-array 10 :element-type 'fixnum :short t)) (SHORT-SIMPLE-ARRAY (SIGNED-BYTE 32) (10)) CL-USER(4): ;; and CL-USER(9): (featurep :64bit) (:64BIT :SMP-MACROS :SSL-SUPPORT) CL-USER(10): (type-of (make-array 10 :element-type 'fixnum)) (SIMPLE-ARRAY FIXNUM (10)) CL-USER(11): (type-of (make-array 10 :element-type 'fixnum :short t)) (SHORT-SIMPLE-ARRAY (SIGNED-BYTE 64) (10))
nil
always
results in a (long) array:
CL-USER(4): (type-of (make-array 0 :element-type nil)) (SIMPLE-ARRAY NIL (0)) CL-USER(5): (type-of (make-array 0 :element-type nil :short t)) (SIMPLE-ARRAY NIL (0)) CL-USER(6):
excl::foreign
always results in
a short array:
CL-USER(6): (type-of (make-array 5 :element-type 'excl::foreign)) (SHORT-SIMPLE-ARRAY FOREIGN (5)) CL-USER(7): (type-of (make-array 5 :element-type 'excl::foreign :short t)) (SHORT-SIMPLE-ARRAY FOREIGN (5)) CL-USER(8):
In all other cases, the "short-ness" of arrays depends only on the :short argument to make-array:
CL-USER(8): (type-of (make-array 5 :element-type 'double-float)) (SIMPLE-ARRAY DOUBLE-FLOAT (5)) CL-USER(9): (type-of (make-array 5 :element-type 'double-float :short nil)) (SIMPLE-ARRAY DOUBLE-FLOAT (5)) CL-USER(10): (type-of (make-array 5 :element-type 'double-float :short t)) (SHORT-SIMPLE-ARRAY DOUBLE-FLOAT (5)) CL-USER(11):
At a low level, and below the level most programmers will ever need to know, some other CL objects retain the same basic structure (and thus the allocation limitations) as short arrays, though these can certainly be reviewed and addressed as necessary in the future.
They are:
These objects should never be arguments to svref, even if they had been punned on simple-vectors in unsafe code (`punned' means declared to be simple-vectors even when they are not). If such punning is still needed for these objects, use ssvref.
Short-arrays are not Common Lisp standard types. Some of the
relationships between short-arrays and normal (long) arrays are
intuitive, but some are not. For example, a short-vector of
element-type character is arrayp, and is short-array-p, but is not stringp, though it is short-string-p (this is because only (array
character (*)) is stringp.
And a short-simple-vector (i.e. of (short-simple-array t
(*))
type) is short-simple-vector-p, but is not simple-vector-p, because only
(simple-array t (*))
is a simple-vector.
Most other relationships between short array types are consistent and type-of, typep, and subtypep know about them.
The list of short array types, classes, and utility functions
follows. The symbols naming them are the standard Common Lisp symbol
names with short-
prepended. All are in the
excl
package.
All short array types are subtypes of
array
, but not subtypes
of any other Common Lisp array type. Their type hierarchy is the same
as the corresponding Common Lisp array type hierarchy.
The various predicates also correspond to their standard Common Lisp counterparts. arrayp and (where appropriate) vectorp return true when applied to short arrays, but no other Common Lisp array predicate returns true when applied to a short array.
short-array
(a type, not documented on its
own page, see array
)
short-array-dimension-limit
short-array-total-size-limit
(a constant)
short-base-string
(a type, not documented on its
own page, see base-string
)
short-bit-vector
(a type, not documented on its
own page, see bit-vector
)
short-simple-array
(a type, not documented on its
own page, see simple-array
)
short-simple-base-string
(a type, not documented on
its own page, see simple-base-string
)
short-simple-bit-vector
(a type, not documented on
its own page, see simple-bit-vector
)
short-simple-string
(a type, not documented on
its own page, see simple-string
)
short-simple-vector
(a type, not documented on
its own page, see simple-vector
)
short-string
(a type, not documented on
its own page, see string
)
short-vector
(a type, not documented on
its own page, see vector
)
There are the following two types. Each is defined by a deftype form, the source is shown.
dual-simple-array
: a type, defined by the form
(deftype dual-simple-array (elem dims) `(or (simple-array ,elem ,dims) (excl:short-simple-array ,elem ,dims)))
dual-simple-vector
: a type, defined by the form
(deftype dual-simple-vector () `(or simple-vector excl:short-simple-vector))
aref and its setf works on short arrays and normal arrays. But the specialized accessors sbit, schar, and svref and their setf's only work on normal arrays (it is an error to pass a short array to them). The following three specialized short array accessors work, in the same way as their Common Lisp counterparts, on short arrays.
In optimized code, care must be taken to match the kind of array with
its accessor; svref will
open-code to a single instruction access that assumes a normal (long)
vector of type t
. If the vector is instead a
short vector, the access might be to a nonexistent slot beyond the
allocation of the short-simple-vector. In the other direction, ssvref will open-code to a single
instruction access that assumes a short array. An inverse-ssvref of
the zeroth "slot" of a normal (long) array will overwrite the length
word, and will result in eventual GC corruption.
An aref in optimized code will
generate the correct code if the declaration is missing or matches the
kind of array that will actually be accessed. If it is unknown
whether the array being accessed will be short or normal (long), then
a declaration of dual-simple-array
or
dual-simple-vector
will generate the right code
(but code which is still much faster than an out-of-line call to
aref or its inverse).
It is strongly recommended, in the face of all of these dangers, that use of short-arrays is kept at a minimum. The space-savings of short-arrays over normal arrays is on average one word per array (depending on the parity of the size; odd-sized short-arrays will save 2 words, and even-sized short-arrays will not save any memory) so the desirability of short arrays is very small when compared to the risks.
Because short strings are not true strings (i.e. are not stringp), short strings are not permitted as arguments to string comparison functions such as string= and string-lessp. An error will be signaled if you pass a short string as an argument to a string comparison function.
If you need to compare short strings with each other or with regular strings, you can use equalp for equality tests. You must write your own functions for greater than or less than tests, such as the following:
(defun my-string-lessp (s1 s2) (let ((l1 (length s1)) (l2 (length s2)) minl) (setq minl (min l1 l2)) (dotimes (i minl) (if (char-lessp (aref s1 i) (aref s2 i)) (return-from my-string-lessp i))) (cond ((>= l1 l2) (return-from my-string-lessp nil)) (t (return-from my-string-lessp l1)))))
X3J13, the ANSI subcommittee chartered to propose a specification for the forthcoming ANSI Common Lisp, has voted to make several changes to Common Lisp's treatment of characters. The intent of these changes is to clean up ideas that are felt not to have worked out in pre-ANSI Common Lisp as well as to allow for Common Lisp to be extensible to international languages. Unfortunately, some of these changes affect backward compatibility and storage efficiency. The result is that Franz Inc. has had to make some user-visible changes that may affect code which explicitly makes arrays or vectors of type character.
X3J13 has removed discussion of bit and font attributes of characters from the Common Lisp language. The string-char type specifier has also been removed from the language by X3J13. Finally, strings are now equivalent to (vector character) for creation purposes. X3J13 allows characters to be attributed with bit/font features as described in CLtL, but in an implementation-dependent way.
ANSI compatible Allegro CL continues to support font/bit attributes of characters. For example, the reader and printer acts on such characters in the pre-ANSI CL way (e.g., #\control-a is #\a with the control bit set, #3\meta-b is #\b with font 3 and the meta bit set). What's more, functions operating on bits and fonts from pre-ANSI CL (e.g., string-char-p, char-bits, char-font, make-char) are available in the cltl1 package, though the use of that package is deprecated.
Because Franz Inc. wants to achieve as much backward compatibility as possible with code using pre-ANSI font/bit attributed characters, and because Franz Inc. also wants to represent strings at least as efficiently as they have been in pre-ANSI versions of Allegro CL, difficulties arise in representing attributed characters in strings (which are now vectors of characters instead of vectors of string-chars). What ANSI-compatible Allegro CL does is to specify that it is an error to store attributed characters in a string. What in fact happens if one tries to do so is that the attributes are stripped. Thus an attributed character that has been stored in an array and extracted is no longer attributed and no longer EQL to its previous value.
Although this behavior violates the spirit of how elements are stored in arrays, this behavior was chosen by Franz Inc. because (a) pre-ANSI CL code using fonts/bits will not have been storing attributed characters into strings since it has always been an error to do so, and (b) representing strings as arrays that can hold attributed characters would have made strings less efficient and incompatible with existing foreign function code that uses strings.
In other words, portable ANSI CL code should not notice this
compromise and pre-ANSI CL code should mostly be able to run as before
with very little source change. The one area where portable pre-ANSI
CL may run into problems is in places where the character type
specifier is explicitly specified in calls to make-array, or to sequence functions that create
a vector. (Such sequence functions include coerce, map, concatenate,
etc.) These places in pre-ANSI CL where the character type specifier
is used should most likely be changed to specify the t
type specifier. In pre-ANSI versions of Allegro CL
(array character) was equivalent to (array t).
Allegro CL has the ability to autoload certain files and modules. In order to keep the size of the system down by excluding parts not always needed, some of Allegro CL is not included in the system when it is built. These parts must be loaded in when they are required. This section describes how that code is loaded in.
Autoloads are triggered by referencing certain objects associated with an unloaded module. Typically, calling a function triggers an autoload, but autoloads can also be triggered by referencing a package or a class associated with an unloaded module. Note that only certain objects associated with a module trigger autoloads. If you reference unloaded functionality that does not trigger an autoload, the functionality may seem to be undefined.
An autoload is an automated form of load. When an autoload occurs, a
message is printed unless *load-verbose*
is nil
, in which case the autoload is done silently. The
autoload message is sent to the stream specified by the variable
*system-messages*
.
All the fasl files which have the potential to be autoloaded are part of the Allegro CL library. All the files are collected into a single file called the bundle file. Its filename is files and its type depends on the version of Allegro CL, but is always some variant of [letter]bu, for example files.bu and files.ebu. The bundle file is located in the Allegro directory. It contains a set of fasl files which can be loaded individually (the whole file is not loaded when a part is). The function bundle-pathname returns the pathname of the bundle file.
Code for some Common Lisp functions and macros (notably
trace, inspect, and step) are contained in
modules separate from the default binary. (The modules are called
:trace, :inspect, and :step.) Whenever any Common Lisp function or
macro is called, the necessary module will be loaded
automatically. Note that using auxiliary features provided as
extensions (such as referring to the variable *trace-print-length*
) will not cause the
module to be loaded. Even though the modules can be automatically
loaded, we recommend explicitly loading those that you need with a
call to require, as described
below.
The code for major extensions, such as the foreign function interface or multiprocessing, also is loaded when needed instead of being in the default Lisp binary. Again, calls to some functions will cause the correct module to be loaded, but we recommend loading the module before using the facility, using require, as described next.
While most modules will be loaded automatically when an important function or macro defined in the module is called, you have to load modules explicitly to use some of the less central functionality. Some users also prefer to explicitly load modules in order to save waiting when the module is actually needed.
To load a module with require, simply enter the form:
(require :module-name)
It is useful to put this form at the beginning of any source file containing code which uses symbols in the module. It is not an error to call require when the module is already loaded.
This section describes implementation details and extensions to Common Lisp operators.
An extension is additional functionality beyond what is specified in the ANSI spec. The section Section 6.1 Extensions to cl:make-package, cl:intern, cl:disassemble, cl:truename, cl:probe-file, cl:open, cl:apropos, etc. describes extensions to a number of SL functions. Usually, these extensions use an additional (non-standard) argument. Portable programs should conditionalize any use of that argument so that it is only used when run in Allegro CL.
An implementation detail either clarifies some part of the spec that is intentionally or unintentionally under specified. The spec usually says that details are left to the implementation when it intentionally under specifies. Unintentional under specification is more subtle: the spec simply says nothing about what should be done in a particular situation. (So for example, should a defpackage call which defines an existing package completely redefine the package according to the new description or should it add features to the package without removing existing features -- see Section 6.12 cl:defpackage and cl:in-package for details on this issue.) A number of subsections discuss such details of various Common Lisp operators (and some variables).
Certain standard Common Lisp functions have been extended in minor ways in Allegro CL. Elsewhere we describe changes to load: Using the load function in loading.htm for the general implementation, Load foreign code with cl:load in foreign-functions.htm (for loading foreign code) and sleep (in Process functions and variables (both models) in multiprocessing.htm, making it work on a per-process basis). Those functions were extended to do something essentially new (load to load foreign functions and fasl files in libfasl mode, sleep to work on single processes). The extensions mentioned in this section refer to changes in the semantics of some Common Lisp functions which affect the way they are ordinarily used. The sort of changes done include allowing strings denoting objects as input as well as the object itself. In some cases we have added boolean variables which control the extended behavior, allowing you to decide exactly how you want Lisp to work.
The following Common Lisp operators are dicussed in subsections of this section:
Arguments: package-name &key use implementation-packages (internal-symbols 10) (external-symbols 10)
We have added some additional keyword arguments to make-package. The implementation-packages keyword argument is an Allegro CL extension described fully in packages.htm. Its value should be a list. Otherwise, this function works as specified in the ANSI specification. The default for the use argument is implementation-dependent. The default in Allegro CL is a list containing one element, the common-lisp package.
The values of the internal-symbols and external-symbols keyword arguments should be positive integers. As implemented, package objects have slots for two hashtables, one for internal symbols and one for external symbols. The internal-symbols and external-symbols arguments guide how large these hashtables should initially be.
Arguments: string &optional packages
Allegro CL may allow a symbol as the first
(string) argument to intern. Standard Common Lisp requires
that the first argument be a string, but specifies no consequences if
it is not. Allegro CL controls the behavior with the variable
*intern-allows-symbol*
, which, if true,
causes intern to also
accept a symbol as its first argument. If *intern-allows-symbol*
is nil
, passing a symbol as the first
argument signals an error.
Arguments: name-or-compiled-function &key absolute references-only recurse start end
The standard disassemble does not have any keyword arguments. The keyword arguments are extensions which are likely not supported in implementations of Common Lisp other than Allegro CL.
In standard CL, name-or-compiled-function should be a function-object, a lambda expression, or a symbol with a function definition. Allegro CL also accepts function names which are lists as well (see Section 9.0 Function specs (fspecs) for a discussion of function names which are lists).
name-or-compiled-function can also be a string. A string is interpreted as naming a foreign (C or Fortran) function. The string must match the name identified by applying nm (or similar system function) to the current symbol table. This is often the result of applying convert-to-lang to the routine name, but there are exceptions -- e.g. Lisp internal routines typically do not have a prepended underscore. name-or-compiled-function can also be a codevector. These are extensions to Common Lisp.
If the value of the absolute keyword argument is
nil
(the default), then relative pc addresses
are given, starting at 0. If the value of
absolute is true, addresses
are given as absolute addresses. Note that these addresses are
consistent within a single disassembly, but any gc activity may have
moved the code vector by the time the disassembly is done.
The recurse keyword argument, if true, causes
internal functions to be disassembled after the specified function. It
defaults to t
if the
name-or-compiled-function represents a function
and if
references-only is nil
,
and neither start, or end is
specified. Otherwise it defaults to nil
.
If the references-only keyword argument is
specified true (its default value is nil
)
then no disassembly is printed. Instead, a list is returned of all
references the function identified by the required argument makes
(from either the function object or the global table) to any Lisp
object. When references-only is
non-nil
, recurse
defaults to nil
.
The start
and end keyword arguments act in the
spirit of the start and end
keyword argument to sequence functions, but the output of disassemble
is not a sequence so the arguments differ from those. Both values, if
specified, should be non-negative integers indicating the pc-offset
where printing of disassembled code should start and stop. The
absolute argument is ignored: start
and end work with respect to
a start of 0 regardless of what the absolute
address is. When start
or end or both are
specified, recurse defaults
to nil
.
Further:
:start 5
is
specified, then the instruction at 4 is printed as the first
instruction.
Here is an example:
cl-user(1): (defun foo (x y) (+ (sqrt (* 2 y)) (log x))) foo cl-user(2): (compile 'foo) foo nil nil cl-user(3): (disassemble 'foo) ;; disassembly of #<Function foo> ;; formals: x y ;; constant vector: 0: sqrt 1: log ;; code start: #x40e922c4: 0: 55 pushl ebp 1: 8b ec movl ebp,esp 3: 83 ec 30 subl esp,$48 6: 89 75 fc movl [ebp-4],esi 9: 89 5d e4 movl [ebp-28],ebx 12: 39 a3 be 00 cmpl [ebx+190],esp ; "thread: stacklim" 00 00 18: 76 02 jbe 22 20: cd 65 int $101 ; sys::trap-stack-ovfl 22: 83 f9 02 cmpl ecx,$2 25: 74 02 jz 29 27: cd 61 int $97 ; sys::trap-argerr 29: 89 45 dc movl [ebp-36],eax ; x 32: 80 7f cb 00 cmpb [edi-53],$0 ; sys::c_interrupt-pending 36: 74 02 jz 40 38: cd 64 int $100 ; sys::trap-signal-hit 40: 8b 9f af fd movl ebx,[edi-593] ; excl::*_2op ff ff 46: b8 08 00 00 movl eax,$8 ; 2 00 51: ff 57 27 call *[edi+39] ; sys::tramp-two 54: 8b 5e 12 movl ebx,[esi+18] ; sqrt 57: b1 01 movb cl,$1 59: ff d7 call *edi 61: 89 45 d8 movl [ebp-40],eax ; excl::local-1 64: 8b 45 dc movl eax,[ebp-36] ; x 67: 8b 5e 16 movl ebx,[esi+22] ; log 70: b1 01 movb cl,$1 72: ff d7 call *edi 74: 8b d8 movl ebx,eax 76: 0b 5d d8 orl ebx,[ebp-40] ; excl::local-1 79: f6 c3 03 testb bl,$3 82: 75 0f jnz 99 84: 8b d8 movl ebx,eax 86: 03 5d d8 addl ebx,[ebp-40] ; excl::local-1 89: 70 08 jo 99 91: 8b c3 movl eax,ebx 93: f8 clc 94: c9 leave 95: 8b 75 fc movl esi,[ebp-4] 98: c3 ret 99: 8b d0 movl edx,eax 101: 8b 45 d8 movl eax,[ebp-40] ; excl::local-1 104: 8b 5f 8f movl ebx,[edi-113] ; excl::+_2op 107: ff 57 27 call *[edi+39] ; sys::tramp-two 110: eb ee jmp 94 ;; Note the start is pc-offset = 3 even though 5 was specified ;; since that instruction includes location 5: cl-user(4): (disassemble 'foo :start 5 :end 34) ;; disassembly of #<Function foo> ;; formals: x y ;; constant vector: 0: sqrt 1: log ;; code start: #x40ed6464: 3: 83 ec 30 subl esp,$48 6: 89 75 fc movl [ebp-4],esi 9: 89 5d e4 movl [ebp-28],ebx 12: 39 a3 be 00 cmpl [ebx+190],esp ; "thread: stacklim" 00 00 18: 76 02 jbe 22 20: cd 65 int $101 ; sys::trap-stack-ovfl 22: 83 f9 02 cmpl ecx,$2 25: 74 02 jz 29 27: cd 61 int $97 ; sys::trap-argerr 29: 89 45 dc movl [ebp-36],eax ; x 32: 80 7f cb 00 cmpb [edi-53],$0 ; sys::c_interrupt-pending ;; When :absolute is true, start and end still use offsets with ;; respect to 0: cl-user(5): (disassemble 'foo :start 5 :end 34 :absolute t) ;; disassembly of #<Function foo> ;; formals: x y ;; constant vector: 0: sqrt 1: log 40ed6467: 83 ec 30 subl esp,$48 40ed646a: 89 75 fc movl [ebp-4],esi 40ed646d: 89 5d e4 movl [ebp-28],ebx 40ed6470: 39 a3 be 00 cmpl [ebx+190],esp ; "thread: stacklim" 00 00 40ed6476: 76 02 jbe 0x40ed647a 40ed6478: cd 65 int $101 ; sys::trap-stack-ovfl 40ed647a: 83 f9 02 cmpl ecx,$2 40ed647d: 74 02 jz 0x40ed6481 40ed647f: cd 61 int $97 ; sys::trap-argerr 40ed6481: 89 45 dc movl [ebp-36],eax ; x 40ed6484: 80 7f cb 00 cmpb [edi-53],$0 ; sys::c_interrupt-pending cl-user(6):
There are other keyword arguments to disassemble but they are not for programmer use.
Arguments: pathname &key (follow-symlinks t)
As specified by section 20.1.3.1 of the ANS, truename must follow symbolic links. Allegro CL
adds the follow-symlinks keyword argument to
control this behavior. truename follows symbolic links if
the follow-symlinks keyword arguments is true
(the default). It returns the symbolic link pathname
if follow-symlinks is
specified nil
.
Note that when pathname
evaluates to a pathname that
references a symbolic link, (delete-file (truename
pathname))
will delete the actual file while (delete-file
(truename pathname :follow-symlinks nil))
will delete the symbolic
link.
Arguments: filespec &key (follow-symlinks t)
probe-file checks to see
whether the file named by filespec exists and
returns its truename if it does. The value of the
follow-symlinks keyword argument is passed as the
value of that argument to truename in order to get the pathname to
return. If filespec evaluates to a pathname that
references a symbolic link, the symbolic link is returned if
follow-symlinks is nil
,
the canonical name of the file if follow-symlinks
is true, the default. See the description of the Allegro CL
implementation of truename just above.
Arguments: file &key direction element-type if-exists if-does-not-exist class follow-symlinks external-format &allow-other-keys
The specification of this Common Lisp function allows a great deal of latitude to the implementation since interfacing with file systems is hard to specify generally. Here we discuss the if-exists, class, and (briefly) the if-does-not-exist keyword arguments. For a discussion of the external-format keyword argument, see Streams in iacl.htm.
The if-exists argument is looked at only
if the
direction argument is specified as
:io
or :output
. In that case the
following values are allowed for if-exists and
have the effect described.
:error
signals an error.
:new-version
is treated just like
:supersede
, which is discussed below. (Unix does
not support file versions.)
:rename
renames the old file to a new file using
the function that is the value of the special symbol *open-rename-function*
. The
variables *open-rename-prefix*
and *open-rename-suffix*
are also
used.
:rename-and-delete
first renames the file following
the conventions of :rename
, then creates the new
file, then deletes the renamed file if the creation was successful.
:overwrite
opens the file for destructive
modification. Although the file pointer initially points to the
beginning of the file, the file is not truncated to zero length upon
opening.
:append
opens the file for destructive
modification. The file pointer initially points to the end of the
file.
:always-append
causes O_APPEND to be used
when opening the file. This means that concurrent writes by any number
of programs will always write to the end of the file. This is useful
for writing to log files. Be warned, however, that you cannot change
the writing file position of a stream opened with
:if-exists
specified to be
:always-append
. The setf of file-position will have no effect on where
writing will occur. The file position does specify where reading
occurs, however.
:supersede
creates a new file that replaces the
existing file.
nil
creates neither a file nor a
stream. nil
is returned.
The if-does-not-exist keyword argument
also accepts the value :always-append
when a file
is opened for output. This value causes the file to be created and
opened using O_APPEND. See the description of the
:always-append
value for
if-exists described just above for details of the
effect of specifying :always-append
.
The open function has been
further extended to take a class keyword
argument. open passes this
argument to make-instance when it creates the stream,
and as with make-instance, the
argument may be a stream class object or a symbol naming such a
class. If the class argument is not supplied or
is nil
, open selects one of the following built-in
classes according to the direction and
element-type arguments:
excl::character-input-file-stream excl::character-output-file-stream excl::character-bidirectional-file-stream excl::binary-input-file-stream excl::binary-output-file-stream excl::binary-bidirectional-file-stream
These classes all contain file-stream
and are
variously mixed with
fundamental-character-input-stream fundamental-character-output-stream fundamental-binary-input-stream fundamental-binary-output-stream
Although the file-stream subclasses returned by open are all instantiable, at present they require hidden initialization (for element-type upgrading, buffer allocation, etc.) and therefore they should only be created using open. It is fine to further specialize them, but you are required to create instances of your specializations of these stream classes using the :class keyword argument to open rather than by calling make-instance yourself.
open is also modified with &allow-other-keys and &rest to pass all keyword arguments as initialization arguments to make-instance. This has the unfortunate side effect of removing error checking for misspelled keyword arguments.
See streams.htm, particularly the discussion of using open to create streams in Implementation of Common Lisp Functions for simple-streams.
When called with :direction :probe
, open essentially works like probe-file and checks to see whether
the file named by file exists and returns its
truename if it does. The value of the
follow-symlinks keyword argument, which is
ignored unless direction is
:probe
, is passed as the value of that argument to
truename in order to get the
pathname to return. If file evaluates to a
pathname that references a symbolic link, the symbolic link is
returned if follow-symlinks is nil
, the canonical name of the file if
follow-symlinks is true, the default. See the
description of the Allegro CL implementation of truename just
above.
Arguments: string &optional package external-only (case-insensitive t)
apropos in Allegro CL accepts
two optional arguments in addition to the single standard optional
argument. The second optional argument is
external-only. If a package designator is
specified as the value of the first (standard) optional argument, only
symbols external in that package will be considered as candidates for
output. If package is specified nil
(some value must be given if
external-only is to be specified), the
external-only is
ignored. external-only defaults to nil
.
CL-USER(1): (apropos :defun nil t) DEFUN [macro] (name varlist &rest body) COMP::PA-DEFUN-PROTO-1 [function] (xform) COMP::QC-DEFUN-IN-RUNTIME [function] (node target cc) COMP::COMPILE-P-DEFUN [function] (form) EXCL::DEFUN-PROTO-1 EXCL::DEFUN-LIKE [function] (xp list &rest args) EXCL::RECORD-SOURCE-FILE-DEFUN [function] (fspec &optional icsp) DEFUN-PROTO [macro] (name varlist &rest body) FF::DEFUN-FOREIGN-CALLABLE-1 [function] (name arglist body) FF:DEFUN-FOREIGN-CALLABLE [macro] (name arglist &rest body) FF:DEFUN-C-CALLABLE [macro] (&whole form &rest args) :DEFUN value: :defun CL-USER(2): (apropos :defun (find-package :excl) t) DEFUN-PROTO [macro] (name varlist &rest body) CL-USER(3): (apropos :defun (find-package :excl) nil) EXCL::DEFUN-PROTO-1 EXCL::DEFUN-LIKE [function] (xp list &rest args) EXCL::RECORD-SOURCE-FILE-DEFUN [function] (fspec &optional icsp) DEFUN-PROTO [macro] (name varlist &rest body)
The third optional argument is case-insensitive. If true (which is the default), comparisons between string and symbol names are done in a case-insensitive fashion. Thus, in an ANSI (case-insensitive, symbols are named with uppercase strings) image,
(apropos "car" (find-package :common-lisp) nil nil) PRINTS nothing (as no symbols in the CL package have "car" in their names) (apropos "car" (find-package :common-lisp)) PRINTS: MAPCAR CAR
And in a modern image (case-senstive, symbols are named with lowercase strings),
(apropos "CaR" (find-package :common-lisp) nil nil) PRINTS nothing (as no symbols in the CL package have "CaR" in their names) (apropos "CaR" (find-package :common-lisp)) PRINTS: mapcar car
When printing output, apropos binds *print-nickname*
to true, so package nicknames
are always used when they exist. For example, the first nickname of
the foreign-functions
package is "ff", and we have:
cg-user(5): *print-nickname* nil cg-user(6): (format t "~S~%" 'ff:def-foreign-call) foreign-functions:def-foreign-call nil cg-user(7): (apropos :def-foreign-call) ff:def-foreign-call [macro] (ff::name-and-options ff::args &key ff::call-direct ...) :def-foreign-call value: :def-foreign-call cg-user(8):
Arguments: string &optional package external-only case-insensitive
Like apropos, as described
just above, apropos-list
accepts two additional optional arguments,
external-only and
case-insensitive. If
external-only is true and a package designator is
specified for the standard optional argument
package, only external symbols in that package
are included in the result. If case-insensitive
is true (the default is nil
), comparisons between string
and symbol names are done in a case-insensitive fashion.
Arguments: stream
The Common Lisp function interactive-stream-p returns true if its argument is an interactive stream, which is a stream "on which it makes sense to perform interactive querying". Allegro CL extends this function so that it is setf'able.
When (setf (interactive-stream-p stream) t) is
evaluated, not only does (interactive-stream-p
stream)
return true, but also any writing that is done is
encapsulated into blocks of output that are forced out by a call to
force-output at the end of the
call. This makes the stream seem like it is unbuffered, yet without
sacrificing as much performance as a raw unbuffered stream would
require, since the actual output takes place only at the end of each
group of write operations.
with-open-file tries to guarantee that the file stream opened for the evaluation of its body is closed, thus avoiding open but unused files. (Such open files can cause an error if the number, set by the operating system, of allowable open files is reached.)
But note that there is a hazard between the time Lisp calls out to the operating system to open a file and the time Lisp sets the stream variable to the newly opened file stream. Between those events, an interruption that causes a non-local exit may leave the file open, but Lisp, lacking any handle on the newly opened stream object, cannot in fact close it.
The risk is small, but can be exacerbated by the following:
Allegro CL implements the time macro so that code in the body is compiled if necessary (and the compiler is present). The macro prints timing information and then the return valkue of the body:
cl-user(2): (defun foo (n) (let ((lis nil)) (dotimes (i 100000) (push (* n i) lis)) lis)) foo cl-user(3): (compile 'foo) foo nil nil ;; ;; This example run on an SMP Lisp so includes a 'cpu time (thread)' ;; line. That line will not appear in non-SMP Lisps. ;; cl-user(4): (time (foo 120034)) ; cpu time (non-gc) 0.003999 sec user, 0.001000 sec system ; cpu time (gc) 0.032995 sec user, 0.001000 sec system ; cpu time (total) 0.036994 sec user, 0.002000 sec system ; cpu time (thread) 0.003999 sec user, 0.000000 sec system ;; SMP Lisps only ; real time 0.038979 sec (100.0%) ; space allocation: ; 99,580 cons cells, 0 other bytes, 0 static bytes ; Page Faults: major: 0 (gc: 267), minor: 388 (gc: 267) (12003279966 12003159932 12003039898 12002919864 12002799830 12002679796 12002559762 12002439728 12002319694 12002199660 ...) cl-user(5):
The information reported is:
The directory function has some keyword arguments added to it to assist in recursive walks down a directory tree. (Note that even though the new argument is not specified, Common Lisp: the Language says the following about directory: `It is anticipated that an implementation may need to provide additional parameters to control the directory search. Therefore directory is specified to take additional keyword arguments so that implementations may experiment with extensions, even though no particular keywords are specified here.')
Arguments: path &key (directories-are-files t) (follow-symbolic-links t)
Returns a list of pathnames matching path,
which may be a pathname, string, symbol or stream. Returns nil
if there is no match.
If the keyword argument directories-are-files
is specified true (the default), this function
will return directories as files (that is pathnames with name and/or
type components true). If the argument is nil
, directories are returned as directories
(pathnames with name and type components nil
). In the latter case it is possible to walk down
a directory tree recursively using directory.
The elements of the list returned by directory is in the same order as returned by the associated system function (e.g. readir() on UNIX).
If directory is given
wildcards, for example "*/*.cl", it will ignore files which are
symbolic links that point to other directories. This prevents directory recursing into these
symbolically named directories. For example, (directory
"*/*.cl")
will no longer, in the face of a `foo' symlink to
a directory, would descend into `foo'. However, When
follow-symbolic-links is non-nil
(the default), directory recurses into
directories pointed to by symlinks when the appropriate "**" (that is,
:wild-inferiors) directory component is used. (This issue
affects UNIX and UNIX like platforms only following symbolic links is
not supported on the Windows implementation.)
directory uses pathname-match-p, which, when presented with wildcards in path (when path is a string), converts the pathname into Allegro CL regular expressions, according to the rules given next. (See regexp.htm for information on regular expression handling.)
. turned into \. * turned into .* ? turned into . ^ prepended onto beginning $ appended onto end
. turned into \. * turned into .[^/]* (or .[^\\]* on windows) ** matches any number of directory levels ? turned into . ^ prepended onto beginning $ appended onto end
The ensure-directories-exist function has two additional keyword arguments: verbose and mode. If verbose is specified true, it prints the fact that a directory is created when one is. The default value for the mode argument is #o777. The value should be a non-negative integer less that or equal to #o777. Any directory cerated with be created with that mode.
The for-as-in-sequence
subclause was added in a
patch release in May, 2014.
The loop macro, cl:loop, is extended to
support for-as-in-sequence
subclauses, which is in
addition to the standard for-as-in-list
and
for-as-across
(for looping over vectors).
In the ANS Section 6.1.2.1 Iteration Control, descriptions are provided for several iteration controls over object types which are suited for iteration. Two of these are elements of lists (The for-as-in-list subclause) and vectors (The for-as-across subclause). But there is no single iterator which will work on either lists or vectors.
Allegro CL has introduced a new for-as-in-sequence
clause, which allows iteration over either lists or simple, general
vectors. It allows for implementational switches from lists to such
vectors and vice versa, and it does so with as little run-time expense
as possible (the restriction to simple vectors allows much faster
performance than would be possible if any type of vector was
allowed). It combines common aspects of the
for-as-in-list
and for-as-across
subclauses.
The template is simplified compared to templates
for for-as-in-list
and for-as-across
and vectors appearing in the
clause have certain restrictions:
for-as-across
and so also not for
for-as-in-sequence
.
(simple-array t (*))
-- a simple vector,
described here,
has no fill pointer, is not displaced to another array, is not
expressly adjustable, and is general, so may contain elements of any
type). Vectors in a for-as-across
clause need not
be simple.
(defun foo (x) (loop for y in-sequence x collect (1+ y))) (foo '(1 2 3)) => (2 3 4) (foo #(1 2 3)) => (2 3 4)
The for-as-in-sequence
only iterates over the top
level of a list or vector. Elements of the list or vector which are
themselves lists of vectors are treated as simple data, but destructuring
works in the
for-as-in-sequence
subclause but only for elements
which are lists:
CL-USER(2): (loop for (x y) in-sequence '((1 2) (3 4)) collect (list x y)) ((1 2) (3 4)) CL-USER(3: (loop for (x y) in-sequence #((1 2) (3 4)) collect (list x y)) ((1 2) (3 4)) CL-USER(4): ;; But this does not work CL-USER(4): (loop for (x y) in-sequence '(#(1 2) #(3 4)) collect (list x y)) Error: Attempt to take the car of #(1 2) which is not listp. [condition type: TYPE-ERROR]
The functions delete, delete-if, delete-if-not, and delete-duplicates have traditionally tried to shorten simple-vectors in-place, so that copies need not be made when items are deleted from these vectors. But to truly be SMP-safe these functions must act more like their remove* counterparts, due to the difficulty in synchonizing the use of the original object efficiently. However, this means that legacy code which assumed that the simple-vector was modified in-place might break.
Decisions can be made at various levels as to whether "in-place"
modification will be done or whether copying will be done instead.
This is controlled by the new variable *delete-in-place*
, and also by a
new in-place keyword argument to
delete,
delete-if,
and delete-if-not. (That
argument is not portable and so should be conditionalized in portable
code.) delete-duplicates
always follows the mandate of *delete-in-place*
and has no new argument.
Defaults have been set so that non-SMP Lisps will still perform the in-place modification, and SMP Lisps will do the copying. Programmers should only specify in-place deleting on SMP if they can guarantee that the vector is not being (and can not be) traversed simultaneously on multiple threads. The problem can (for example) arise when one thread changes the last elements of a vector without noticing another thread has shortened the vector. The change can then modify (illegally) the header of an entirely different Lisp object with the result that the Lisp heap becomes corrupted.
The new arguments to certain deletion functions and the
new *delete-in-place*
variable do not affect the behavior of these functions on list
arguments. However, (and this has always been true in a
multiprocessing Lisp) although failures are less likely when deleting
elements from a list compared to deleting elements from a vector (it
is easy, as noted in bold above, to modfy an illegal location when a
vector is shortened, but not when deleting elements from a list),
there are ways that deletion or modification in one thread and
accessing in another can cause unspecified (and unexpected) behavior
with lists.
Correct code should use the return value of the delete functions.
It is much less likely, but the same problem described in this section can occur in a non-SMP Lisp. Good coding practice says do not use in-place modification in any multiporcessing Lisp (SMP or not) where multiple threads can traverse the sequence.
We have extended the #+ and #- reader macros to accept
(version>= N [ M]) as an argument. It is interpreted to mean that
the form following will only be read if the version (also called
release) of Allegro CL is greater than or equal to N.M. The N must be
supplied. The M is optional. Both must be integers. With #+,
version>= signifies read the next form only if the version is
greater than or equal to N.M. With #-, it means read the next form
only is the version is less than N.M. For example, because of an X3J13
change, the element type for an array of characters is
character
starting in release 4.1 and
string-char
in earlier releases. To have code work
in all Allegro CL releases, do the following:
(make-array 3 :element-type #+(version>= 4 1) 'character #-(version>= 4 1) 'string-char)
Warning: while most Common Lisp implementations (including Allegro CL prior to version 4.1) ignore `(version>=...)', it is possible that an implementation would signal an error upon encountering it. As a workaround for truly portable code, use:
#+(and allegro-version>= (version>=...))
Because :allegro-version>= is (presumably) only on the *features* list of Allegro CL 4.1 and later, this will fail in all versions without version>= having to have a definition.
This standard Common Lisp variable can be used with the #+ and #-
reader macros to conditionalize code for different Lisp
implementations and releases. The exact value is different in every
version of Allegro CL. Here are some useful values which may or may
not be in your version. Please check the value of *features*
in your version to
see exactly what is there. The function featurep can be used to test whether a feature
is present or not.
This is a partial list.
Feature |
Meaning and use |
:allegro |
Unique to Allegro CL. Present in all versions on all platforms. Use this to distinguish Allegro CL from other Lisp implementations. |
:64bit |
Present in 64-bit images, absent in 32-bit images. |
:ignore |
Absent in all versions on all
platforms. Thus a form marked #+ignore
is never evaluated. Used, for example, in
custom.cl. |
:x3j13 |
Purports to conform to some version of Common Lisp specified by the ANSI X3J13 committee. Present in Allegro CL since version 4.2. |
:cltl2 |
Purports to conform to Common Lisp: the Language, 2nd ed. Since ANSI Lisp has diverged, :x3j13 and :cltl2 should not both be present. Not present in Allegro CL 4.2 or later. Present in some earlier versions. |
:draft-ansi-cl-2 |
Purports to conform to the second draft ANSI standard. Allegro CL does so, so :draft-ansi-cl-2 is present in Allegro CL 7.0 |
:ansi-cl |
Purports to conform to ANSI Common Lisp standard. The standard is now (since early 1996) final. Present in Allegro CL starting with version 4.3. |
:dynload |
Foreign loading is done by dynamic linking of shared libraries/objects. The next several features are types of dynamic loading. See foreign-functions.htm. |
:dlfcn |
Uses dlopen() to link foreign code. Present, for example, on Solaris. See foreign-functions.htm. |
:dlwin |
Uses LoadLibrary to link foreign code. Windows machines only. See foreign-functions.htm. |
:dlmac |
Uses the Mac OS X system loader NSLoadModule to link foreign code. Mac OS X machines only. See foreign-functions.htm. |
:dlld |
Loads .o files into image with ld. No Allegro CL version uses this. |
:ics |
Supports International Character sets. Characters are 16-bits (rather than 8 bits). Allegro CL comes in both International and non-International versions (the International version is standard). Use this feature to distinguish the versions. See iacl.htm. |
:os-threads |
When present, each Lisp thread
executes on a distinct os thread within the os
process. Stack-allocated data remains in place as long as it is
in scope. Thread-specific foreign initializations may need to be
done in each Lisp thread, depending on the requirements of the
specific foreign library. Always present when :smp is present.
When absent (:smp will also be absent), all lisp threads share one os thread. The stack data for the currently executing Lisp thread occupies the real os stack; stack data for other lisp threads is saved in other areas of memory until the other thread is to be executed again. Stack-allocated data (whether foreign or Lisp) is only guaranteed to be at its allocated address when the allocating lisp thread is executing. Thread-specific foreign initializations probably need to be done just once for the whole os process. See multiprocessing.htm and smp.htm. |
:smp |
When present, the Lisp allows true simultaneous execution of multiple Lisp
threads on multiple cpus. Even if the host os allocates a single
cpu to the lisp process, different Lisp threads can interleave
execution arbitrarily. Checks for signals, timeouts, and
process-interrupts happen at safe-points.
When absent, the Lisp allows execution of multiple Lisp threads, but only one such thread at a time can be executing Lisp code. Interleaving of thread execution happens at safe-points, as do checks for signals, timeouts, and process-interrupts directed at that thread. |
:smp-macros |
When present, macros associated with SMP are defined. Always present when :smp is present. Useful for conditionalizing code with the macros for versions prior to SMP implementation. |
:mswindows |
Appears in versions running on Windows machines. Use #-mswindows for Unix. |
:sparc |
This feature appears on versions that run on machines with a Sparc processor (e.g. Sun 4's and Sparcstations). A similar platform-naming feature appears in all implementations and allows differentiating between machines. Look for the feature in your version. |
:big-endian |
Platform uses the big-endian method of representing numbers. |
:little-endian |
The platform uses the little-endian method of representing numbers. |
:verify-stack |
Checking how close the stack is to
overflowing is expensive. See verify-stack-switch .
|
:allegro-vN.M |
Present in Allegro CL version N.M. (Examples :allegro-7.0, :allegro-8.0, :allegro-8.1, etc.) See also #+(version>=...) reader macro defined Section 6.7 Reader macros and cl:*features* above. Both it and this feature are useful for conditionalizing code to run on different releases of Allegro CL. |
Assume :allegro
is on the *features*
list and that
:foo
is not. Consider the following two forms and
their evaluations:
;; CASE 1 (list #+allegro :allegro #-allegro #+foo :foo #-foo :default) Versions of Allegro CL prior to 8.0 return (:allegro :default) Allegro CL 8.0 and many other implementations return (:allegro) ;; CASE 2 (list #+allegro :allegro #-allegro #+foo :foo) Versions of Allegro CL prior to 8.0 return (:allegro) Allegro CL 8.0 and many other implementation signal an error
We will explain these disparate behaviors below, but first we
recommend that conditions be nested using not, or, and
and within the #+
or #-
test expressions (the expression which follows the
#+
or #-
) as that is always
unambiguous in any Lisp. Thus the first conditional below implements
the old Allegro CL behavior and the second implement the current
behavior:
(list #+allegro :allegro #+(and (not allegro) foo) :foo #-foo :default) (list #+allegro :allegro #+(and (not allegro) foo) :foo #-(or allegro foo) :default)
Using nesting within the test expression for Case 2 should make clear
what is desired when :allegro
holds and
:foo
does not -- presumably:
(list #+allegro :allegro #+(and (not allegro) foo) :foo)
In older Allegro CL implementations, when a conditional fails (like
#-allegro fails), a conditional in the associated form (the one that
will be ignored) is not further considered. Thus that conditional and
its associated form are taken to be the form to be ignored. So in the
first example, #-allegro #+foo :foo
is considered
to be a (failing) conditional and its associated form. It is ignored
and the reader then encounters #-foo :default
. The
#-foo
conditional succeeds so the subsequent form
-- :default
-- is evaluated.
Other Lisp implementations resolve the conditionals following a
conditional as part of determining what the form associated with a
conditional is. Allegro CL has been changed to match that behavior. As
a result, conditionals following a conditional (i.e. nested
conditionals) are considered and resolved as part of determining the
form that follows a conditional, the form that should be ignored (when
the original conditional fails) or evaluated (when it succeeds). So in
#-allegro #+foo :foo #-foo :default
the inner
conditionals #+foo :foo #-foo :default
are resolved
to :default
. Thus #-allegro #+foo :foo
#-foo :default
resolves to #-allegro
:default
which is then ignored.
In the second example, #-allegro #+foo :foo
resolves to #-allegro
which signals an error
because no form follows the #-allegro
conditional,
and that is erroneous code. (The conditional doing the nesting within
the test expression, show above, does not error.)
We believe (although we do not present our analysis here) that the ANSI standard is ambiguous on the handling of these cases and so both the older Allegro CL behavior and the newer behavior are within standard. However, since the #+/#- conditonals are designed to allow for using the same code in various implementations of Common Lisp, we believe it is most important that all implementation do the same thing. Since other implementations of Common Lisp resolve inner conditionals to produce the form that outer conditionals apply to, Allegro CL has been changed (starting in release 8.0) to do that as well. Again, we recommend doing the nesting in the test expressions rather than nesting #+/#-'s.
The change in the handling of nested #+/#-'s is a
non-backward-compatible change in Allegro CL 8.0, and a rather obscure
one which may cause difficult to diagnose errors in user code which
has heretofore worked correctly. To mitigate this, in Allegro CL 8.0
(and later), a warning is signaled when nested conditionals are
detected. This warning remarks on the behavior change. The warning is
suppressed when the variable *warn-on-nested-reader-conditionals*
is set to
nil
(its initial value is t
). Users who want to revert to the old behavior (not
resolving inner conditionals before applying outer) can do so by
setting the variable *sharp-plus-de-facto-standard-compatible*
to
nil
(its initial value is also t
). We do recommend that user change their code to
conform to the new behavior where that is possible rather than
reverting to the old behavior.
Allegro CL uses the Mersenne-Twister algorithm, MT179937. MT179937 is described in detail in the paper "Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator" by Makoto Matsumoto (Keio University/Max-Planck-Institut fuer Mathematik) and Takuji Nishimura (Keio University), which appeared in the issue 1/1998 of the ACM Transactions on Modeling and Computer Simulation.
The required argument to random
must be a positive real (integer or float). In the Allegro CL
implementation, there are internal functions that are called based on
the type of the required argument. If the compiler trusts declarations
(see trust-declarations-switch
) and the type of
the required argument is known at compile time (because its type is
declared or it is a constant), the call to random will be transformed into a call to the
appropiate internal function in most cases. If the type is not known
at compile time or declarations are not trusted,
random is called directly and
dispatches to the correct internal function after determining the type
of the required argument.
Because random may be called by any Lisp function at any time, there can be no guarantee that the sequence of numbers seen by your calls to random will be the same each time you invoke Lisp even if your actions are seemingly identical. However, absent specific action on your part, often the values returned by random are the same from invocation to invocation. If either repeatability or ensuring different runs are different are important to you, you should manage random by specifying the optional random-state argument with random-state objects you have created and stored (see make-random-state). Printed versions of random-state objects are readable so values can be stored in text files.
When a new process is created, the value of *random-state*
may be bound as part of
the initial bindings for the process (see
mp:make-process and
mp:process-run-function). *random-state*
is one of the variables
included in the suggested list of bindings which is the value
of *required-top-level-bindings*
,
but that list
is used for processes you create only if you specify that it be
used. The binding is to a copy of an existing random-state
object. This means that if that list is used, different processes may
start with copies of the same random-state object or with
random-states that produce similar (i.e. slighly displaced) random
number sequences. This may or may not be what is required for your
application. As we suggest with managing random numbers in general, we
suggest that if the nature of random sequences is important to your
application, you manage the random sequences for processes that you
create by creating your own random-state objects
(with make-random-state) and using them in
the processes you create.
Arguments: number &optional state
Returns a pseudo-random number uniformly distributed between 0 and (- number 1) if number is an integer and between 0 (inclusive) and number (exclusive) if number is real but not an integer. number must be real and positive. state should be a random-state object. If supplied, it will be made the state while the returned value is calculated.
Arguments: &optional state seed
This standard Common Lisp function returns a random-state object. If
state is already a random-state, it is
returned. If state is t
(and
seed is nil
), make-random-state uses
get-universal-time for its
starting value. We have ensured that even in a tight loop, different
states will be produced by each call in the loop.
The seed argument is provided as an extension to
standard Common Lisp. If state is specified as
t
, and seed is given and
is an integer, then instead of using its own internal method for
generating a seed, that specified seed is
accepted and used to create the new random-state. Two such calls to
make-random-state with the same seed will produce equivalent
random-states. Only the least significant 32 bits of the integer are
used to seed the random state.
This capability allows the user to generate starting seeds from any random-number generation source. The question of how long that source requires to generate truly random data is the user's responsibility. (Thus /dev/random, if available, may block indefinitely waiting for the entropy pool to be replenished. /dev/urandom can also be used to generate random seeds, if it is available).
Arguments: &key test size rehash-size rehash-threshold hash-function values weak-keys
Hash tables with standard tests (eq, eql, equal, and equalp) have been optimized in Allegro CL to make putting values into and getting values from a hash table fast. eq hashtables are the fastest, followed closely by eql, and then equal and equalp.
The maximum size of a hash table is one less than the value of array-dimension-limit
. In safe
code, if the value specified by the size is
greater than or equal to array-dimension-limit
, then array-dimension-limit
minus 1
will be used instead and a warning will be signaled.
Allegro CL has also extended make-hash-table in several ways:
The hash-function keyword argument allows further specialization when standard functionality is inefficient (usually because of excessive collisions caused by bunching of the hash codes of the data). Code that uses the hash-function argument is not portable Common Lisp, of course.
If specified, the value from hash-function must be a symbol naming a function of one argument in the global environment which reproducibly returns an integer in the correct range when applied to any Lisp object intended to be used as a hash key. (The value must be a symbol, not a function object.)
The correct range is between 0 and (1- (expt 2 24)) (inclusive) in 32-bit Lisps and between 0 and (1- (expt 2 32)) (inclusive) in 64-bit Lisps. Reproducibly here means the function will return the same value on equivalent objects whenever it is called. The consequences of returning a value outside the correct range are undefined (and so may result in an incorrect answer or cause an error or program failure).
hash-function defaults to sxhash except when test is one of the four standard tests (eq, eql, equal, equalp) when hash-function defaults to an internal function optimized for that test. (For equal and equalp, the hash-function is an internal version of sxhash.)
A good hash-function will not only conform to the requrements of the
hash-function
argument, but will make efforts to
distribute the hash-codes evenly across the objects which will be used
as keys.
To aid in hash-code generation, an unexported function is provided which is otherwise undocumented: excl::hash-table-stats, which accepts as its only argument a hash-table, and which prints various statistics about that hash-table, including a histogram at the end of how hard it is to access each key (i.e. how far the key resides from its hash-code position). A long histogram is not good; the shortest histogram has only one entry at distance 0 (with all of the keys at that distance) which means that the current distribution of keys is perfect and the access is linear. A long histogram means that the hash code generation has poor distribution, likely either not as random as expected, or else bunched up close to zero.
The value of test must be a symbol naming
a function of two arguments in the global environment. This function
will be passed two keys, and should return t
if the keys are equivalent and nil
if the
keys are not equivalent. The standard values
for test are eq, eql,
equal,
and equalp (or, for these four
functions only, the associated function
objects #'eq
etc.) but any test function can be
specified. (But note (1) that symbol
is reserved
for internal use so test should not be specified
'symbol
in application or user code; and (2) the
value must be a symbol naming a function, not a function object; the
four standard function objects listed just above are accepted as
values but no other function objects.) If
hash-function is specified, it is the
programmer's responsibility to ensure the test function and the hash
function work together correctly and consistently.
weak-keys defaults to nil
, which specifies the default behavior. When
weak-keys is specified as t
, the keys of the resulting hash table are treated
specially by the garbage-collector: when a key in such a hash table
has no more references to it, the entire entry is removed from the
hash table, and the hash-table-count is decremented. This entry
removal will occur regardless of whether :values
:weak
is specified (which by itself will never affect the
hash-table-count, but only the value of an entry). See
gc.htm for information on weak objects.
If weak-keys is given the value
:tenurable
, then the key vector (the part of the
weak-key hash-table that is normally kept in newspace) is allowed to
be tenured. Any other true value for weak-keys
causes the key vector to be forced to stay in newspace (but it is best
to use t
as this allows other non-nil
values which have special meaning to be added
later). The :tenurable
option allows the amount of
data copied between newspace halves to remain smaller than if the key
vector were forced to remain in newspace. This difference can be large
if the hash-table is large. Allegro CL now uses this option
internally. If a tenurable weak-keys hash-table must be rehashed due
to growth, the new key vector is allocated in newspace, but is still
allowed to be tenured. (This means the vector is not created with
:allocation :old
described below.)
The downside of tenuring the weak-key vector is that
references to the values will remain until a global garbage collection
examines the weak-key vector. An untenured weak-key vector is examined
whenever there is a scavenge. Global gc's are typically rare, but
scavenges occur regularly. A decision to use the
:tenurable
option should take this into
consideration.
values can be t
(the default), :weak
, or nil
.
When values is t
, the
hash table will contain both a key and a value for each entry (that
is, it will be a normal hash table). As said above, t
is the default value for
values.
When :values :weak
is specified, then the
hash table will hold a value only as long as it is referenced
non-weakly by some other object. If no other objects reference the
value, it becomes nil
and a gethash on the key will return nil
for the value (the value is collected by
the gc).
;; We create a :values :weak hashtable: cl-user(26): (setq ht (make-hash-table :values :weak)) #<eql hash-table with weak values, 0 entries @ #x48aef52> ;; We create an object to store aa a value: cl-user(27): (setq a (list 1 2 3)) (1 2 3) ;; We store the list as the value of the key 100: cl-user(28): (setf (gethash 100 ht) a) (1 2 3) ;; And the list is returned when we ask for it: cl-user(29): (gethash 100 ht) (1 2 3) t ;; We break the link from the symbol A to the list: cl-user(30): (setq a nil) nil ;; We break the links from variables like *, **, and *** to the list ;; (this works here but be aware that links may exist that you are ;; unaware of, and it make take longer for those links to disappear). cl-user(31): t t cl-user(32): t t cl-user(33): t t cl-user(34): (gc) cl-user(35): (gc) ;; Now when we get the value associated with 100, it is NIL cl-user(36): (gethash 100 ht) nil t ;; Note, second value is T as 100 still has a value. But the ;; value is now NIL, not the list which was the original value. cl-user(37):
When :values nil
is specified, a sans
values hash table is created, and only keys are stored. gethash returns the key as its first
return if the key is in the table, and t
as
the second value in that case. As usual, gethash returns nil
and
nil
if the key is not in the table. You can
use setf and gethash to store a key. You must specify a value
but that value is ignored. You can also use the function excl:puthash-key to store a key in
the table.
On sans-value hash tables, maphash will call its argument function with the key as both arguments (as the key argument and as the value argument), as there is no value to pass.
One use of :values nil
(sans-value) hash tables is
to identify a set of objects, such as those objects which have a
particular property, in a space efficient way. Suppose, for example,
you have many instances (millions of them) of a particular class, and
only 20 are XYZ-positive. You could have an
xyz-positive
instance slot in the class, but that
could use megabytes of space. A sans-value hash table with the 20
objects as keys uses just a few hundred bytes. That table could be the
value of a class slot of the class and a method that looked to the user
like an ordinary reader could test whether an instance was in the hash
table or not, while a writer could add an instance to the hash table.
Sans-value hash tables are also a good way to store conses. If you
have a bunch of conses you will need many times, place each as you
first create it as a key into a sans-value hash table with the
appropriate test function (say equal). Then, if you need that
cons, create one and test it using excl:puthash-key or gethash, and always using the return value
(unless nil
in the case of gethash) and discarding the test value. Only one
permanent copy of the cons will then be stored no matter how may you
create. (See the second example below.)
;; We create a sans-value hashtable: cl-user(49): (setq svht (make-hash-table :values nil)) #<eql hash-table (sans values) with 0 entries @ #x4a94bb2> ;; We store as keys all CL symboles with more than 3 e's in ;; the symbol name. Note we use puthash-key to store the key. ;; We do not need a value because being in the hashtable indicates ;; the key has the desired property (more that 3 e's). ;; cl-user(50): (do-external-symbols (s (find-package :cl)) (if (> (count #\e (symbol-name s) :test 'char-equal) 3) (puthash-key s svht))) nil ;; There are 39 such symbols: #x4a94bb2 cl-user(51): svht #<eql hash-table (sans values) with 39 entries @ #x4a94bb2> ;; We use MAPHASH to print out the 39 symbols. Note the value ;; passed to the MAPHASH argument function is the key (that ;; is, the key is passed as both the K and the V arguments). ;; We have added line breaks for clarity in some cases cl-user(52): (maphash #'(lambda (k v) (format t "~S, value is ~S~%" k v)) svht) integer-decode-float, value is integer-decode-float least-negative-normalized-single-float, value is least-negative-normalized-single-float set-difference, value is set-difference update-instance-for-different-class, value is update-instance-for-different-class double-float-negative-epsilon, value is double-float-negative-epsilon make-sequence, value is make-sequence make-instances-obsolete, value is make-instances-obsolete stream-element-type, value is stream-element-type least-negative-normalized-double-float, value is least-negative-normalized-double-float delete-package, value is delete-package least-negative-normalized-long-float, value is least-negative-normalized-long-float delete-file, value is delete-file array-element-type, value is array-element-type upgraded-array-element-type, value is upgraded-array-element-type encode-universal-time, value is encode-universal-time least-negative-normalized-short-float, value is least-negative-normalized-short-float internal-time-units-per-second, value is internal-time-units-per-second type-error-expected-type, value is type-error-expected-type ensure-generic-function, value is ensure-generic-function delete-duplicates, value is delete-duplicates define-setf-expander, value is define-setf-expander least-negative-single-float, value is least-negative-single-float read-sequence, value is read-sequence get-decoded-time, value is get-decoded-time concatenated-stream-streams, value is concatenated-stream-streams invoke-restart-interactively, value is invoke-restart-interactively read-preserving-whitespace, value is read-preserving-whitespace get-internal-real-time, value is get-internal-real-time least-positive-normalized-double-float, value is least-positive-normalized-double-float decode-universal-time, value is decode-universal-time *compile-file-truename*, value is *compile-file-truename* least-negative-double-float, value is least-negative-double-float nset-difference, value is nset-difference ensure-directories-exist, value is ensure-directories-exist make-concatenated-stream, value is make-concatenated-stream update-instance-for-redefined-class, value is update-instance-for-redefined-class write-sequence, value is write-sequence least-positive-normalized-single-float, value is least-positive-normalized-single-float single-float-negative-epsilon, value is single-float-negative-epsilon nil ;; GETHASH works as usual but returns the KEY as if it ;; were the value: cl-user(53): (gethash 'write-sequence svht) write-sequence t ;; You can use SETF of GETHASH instead of PUTHASH-KEY. Note ;; the value specified (10 in this case) is discarded: cl-user(54): (setf (gethash nil svht) 10) nil ;; GETHASH returns the key as the value. The value specified ;; just above (10) is not stored so is not available: cl-user(55): (gethash nil svht) nil t ;; NIL is returned because the KEY is NIL. When the key is NIL, ;; you must look at the second return value to see if NIL is ;; in the hash table. cl-user(56): ;; In the second example, we create a EQUAL sans-value hash table ;; and store some conses in it. We create a new cons and use ;; PUTHASH-KEY to store it if necessary. PUTHASH-KEY returns ;; the stored cons if there, or the cons if just stored. cl-user(59): (setq cons-storer-ht (make-hash-table :test 'equal :values nil)) #<equal hash-table (sans values) with 0 entries @ #x4b78e3a%gt; ;; We put some conses in the hash table: cl-user(60): (puthash-key (list 'baltimore 'md) cons-storer-ht) (baltimore md) cl-user(61): (puthash-key (list 'boston 'ma) cons-storer-ht) (boston ma) cl-user(62): (puthash-key (list 'berkeley 'ca) cons-storer-ht) (berkeley ca) cl-user(63): (puthash-key (list 'reno 'nv) cons-storer-ht) (reno nv) ;; Here is a cons. We put it in the hashtable if necessary. ;; PUTHASH-KEY returns the one there if present: cl-user(64): (setq a (list 'boston 'ma)) (boston ma) cl-user(65): (puthash-key a cons-storer-ht) (boston ma) ;; Note the new one is not the one returned: cl-user(66): (eql a *) nil ;; So we break the link to the new one, and use the stored ;; one so only one copy is live in the image: cl-user(67): (setq a **) (boston ma)
Arguments: dims &key allocation element-type weak short [and other standard CL keyword args not listed here]
allocation is discussed first and then weak. short is discussed briefly after the discussion of weak and in detail in Section 3.0 Arrays and short arrays.
allocation: make-array, a standard Common Lisp function, has
been extended to accept the allocation keyword
argument. The value of this argument must be one of the following
keywords (the default is :new
, which produces the
behavior of earlier releases).
Value of allocation argument | Meaning |
:new |
Allocate the new array data in new space (the usual behavior). Any array element type accepted. This is the default. |
:old |
Try to allocate the new array data in old space
immediately (without waiting for
it to survive for the required number of scavenges).
Any array element type accepted.
If there is not enough contiguous oldspace available to allocate the array, it will be allocated in newspace. resize-areas can be used before the allocation in order to ensure that there is enough oldspace available. |
:static |
Allocate the new array in aclmalloc (foreign) space. The array will
never be touched by the garbage collector and must be deallocated
explicitly. The arrays must have a specialized element type since
arrays of type t may contain pointers that
the garbage collector may need to update. See the list of array types
in Section 2.0 Data types and array types for a list of
specialized array types. Note that if the upgraded-array-element-type of an
element type is t , that array may not be
allocated :static or :malloc .
You must explicitly free the space if it is no longer needed, as
described below. |
:malloc |
|
:static-reclaimable | Allocate the new array data in aclmalloc (foreign) space and the header in Lisp space. The data will never be touched by the garbage collector but it will be deallocated when there are no pointers from Lisp (using a finalization). Only specialized arrays (not arrays of type t) can be allocated in this way, as with :static/:malloc allocations. See the description of those allocation types for more details. |
:lispstatic-reclaimable
|
Allocate the new array in aclmalloc (foreign) space. The array will never be touched by the garbage collector (except to update pointers back into Lisp space) until there are no pointers from Lisp, at which point the whole array will be deallocated explicitly. Any element-type may be specified for the array. |
allocation is not a standard Common Lisp argument to make-array so programmers may wish to conditionalize it with #+allegro to preserve code portability.
Having created a static array, you may wish to free it. To do this, first pass the array to the function lispval-other-to-address, which will return an address (an integer). That address can be passed to aclfree. Note: if you reference the array after it has been freed, you will get garbage values. If you set a value in the array after it has been freed, you may cause Lisp to fail.
weak: make-array, a standard Common Lisp function, has
been extended to accept the weak keyword
argument. weak is not a standard Common Lisp
argument to make-array so
programmers may wish to conditionalize it with #+allegro to preserve
code portability. weak may be true (meaning
create a weak array) or nil
(meaning create a
standard array). The default is nil
.
A Lisp object becomes garbage when nothing points to or references it. The way the garbage collector works is it finds and identifies live objects (often then moving them somewhere). Whatever is left is garbage. Weak arrays allow pointers to objects which will not, however, keep them alive. If one of these pointers exists, the garbage collector will see the item and (depending on the circumstances), either keep it alive or abandon it.
If you specify weak true, you cannot specify
the non-standard allocation argument or the
standard displaced-to argument. The only values
accepted for the standard element-type argument
are those for which no specialized array type for that element-type is
defined (i.e. upgraded-array-element-type applied to
element-type should return t
, which
in essence means you should not specify element-type).
short: Allegro CL supports two fundamental kinds of arrays: standard and short. Short arrays (equivalent to the array type in releases prior to 7.0) have a smaller maximum size than standard arrays. See Section 3.0 Arrays and short arrays for details. When :short t is specified, a short array is produced. Otherwise a standard array is produced.
See Weak arrays and hashtables in gc.htm for more information on weak arrays.
Arguments: pathname &key syntax
cl:namestring takes a pathname
designator and returns the full namestring of the pathname. Allegro
CL adds an additional keyword argument:
syntax. The value of syntax
can be nil
or :unix
. The
behavior of the syntax argument is different on
Unix and Unix-like platforms and on Windows.
The syntax argument is ignored.
If syntax is :unix
, any
backward slashes in the pathname are converted to forward slashes. If
syntax is nil
(the
default), no slashes are converted and cl:namestring behaves normally.
Thus, on Windows only,
(namestring "\\ftp\\pub\\patches\\8.0\\ftp.001" :syntax :unix) returns "/ftp/pub/patches/8.0/ftp.001" while (namestring "\\ftp\\pub\\patches\\8.0\\ftp.001") returns "\\ftp\\pub\\patches\\8.0\\ftp.001"
The argument was added to assist ftp functions called from Windows. Functions like map-over-ftp-directory called on Windows generates pathnames of the files in an ftp directory, but these generated pathnames use Windows syntax (with backward slashes delimiting directories). In order for these pathnames to be used in calls to other ftp functions, such as ftp-stream-file-mod-time, they must be first converted to Unix syntax. Users writing their own mapping functions for ftp directories may find this added feature of cl:namestring useful. The ftp client module is described in ftp.htm.
Arguments: defined-package-name &rest options
The specification of defpackage is silent on whether, when there are two defpackage forms for the same package, the second should augment the first or the second should replace the first.
Consider, for example, the following two defpackage forms:
(defpackage :newpack (:use :excl :cl)) (defpackage :newpack (:use :net.uri))
What is the package-use-list after the second defpackage form returns: a list of three packages (excl, common-lisp, and net.uri) or a list of a single package (net.uri)? Allegro CL augments the package specification rather than replacing it, as illustrated by the following transcript:
cl-user(1): (defpackage :newpack (:use :excl :cl)) #<The newpack package> cl-user(2): (package-use-list (find-package :newpack)) (#<The excl package> #<The common-lisp package>) cl-user(3): (defpackage :newpack (:use :net.uri)) #<The newpack package> cl-user(4): (package-use-list (find-package :newpack)) (#<The net.uri package> #<The excl package> #<The common-lisp package>)
If you use a symbol (other than a keyword) to specify a value which is
eventually converted into a string (such as the package
name foo
in (defpackage foo)
),
then the macroexpansion of the defpackage form will reference
the uninterned symbol named
foo
, not foo
internal in some
package. This makes little difference to the Lisp which processes,
either evaluating or compiling, the defpackage form
-- foo
will end up being interned in the current
package when the form is read -- but does make a difference to Lisp
images which simply read the fasl (compiled Lisp) file which contains
the defpackage form.
This means that you can use symbols for names in defpackage
forms in your application files, compile those files, and when you
later use the compiled files to build your application, it will not
have package name spaces cluttered by these symbols. Using a symbol
has the advantage that it finesses the case-mode
issue. (defpackage foo)
creates the "FOO" package
in an ANSI Lisp and the "foo" package in a modern Lisp
(see case.htm).
Arguments: package-name
The Common Lisp macro in-package changes the value
of *package*
to the
package designated by package-name. If
package-name is a symbol, the macroexpansion of
the in-package form converts that symbol reference to an uninterned
symbol of that name. See the discussion under the heading Treatment
of string designator arguments named by symbols in the description
of defpackage above for why this is a useful feature.
Arguments: stream
Allegro CL allows stream to be a pathname or a namestring as well as a stream open to a file (ANSI CL specifies only a stream open to a file). For a pathname or a namestring argument, the file-length function returns the size (number of octets, that is 8-bit bytes) of the associated file.
We also do not signal an error when the argument to file-length is a string stream or a buffer stream (instead of just a stream open to a file). See the discussion of file-length in Section 11.0 Conformance with the ANSI specification for further details.
There are two implementation details for cl:file-write-date:
mtime
(the modification time on UNIX) of the
file. On Windows, the comparable value is set.
nil
is returned (rather than
an error being signaled).
The fact that nil
is returned when the
argument file does not exist is arguably an ANSI non-compliance. The
Spec says: "An error of type file-error is signaled if the file system
cannot perform the requested operation". But it also says: "returns
nil if such a time cannot be determined". Returning nil
in this situation is longstanding behavior in
Allegro CL and is being maintained.
In Allegro CL, cl:lisp-implementation-version returns two value. The first is a string which is of the form
"[Version number] [[Platform]] ([Date and time of build])"
For example
"8.1 [64-bit Linux (AMD64)] (Feb 7, 2007 14:55)"
The second return value is a list of strings (there may be only one) that identify the Allegro shared library build version. For example:
("lisp_build_NNN")
NNN
is an integer which is increased with each new
build. (The library is often updated between releases because of
patches.) So, a call looks like:
cl-user(2): (lisp-implementation-version) "8.1 [64-bit Linux (AMD64)] (Feb 7, 2007 14:55)" ("lisp_build_1") cl-user(3):
These examples are for illustration only. The values you see will be different. This information may be helpful when trying to identify a potential problem, allowing users at different sites to be sure they are running the same versions of all parts of Allegro CL.
cl:function-lambda-expression
returns three values: the defining lambda expression, if available
(nil
is returned if it is unavailable);
information on whether the function was or was not defined in the null
lexical environment; and information on the function's name. Here we
discuss when the first returned value might be
non-nil
:
*save-function-lambda-expression*
is true, the
source for the first returned value will be taken from there.
save-source-level-debug-info-switch
compiler switch is true), then it may be possible
for cl:function-lambda-expression to use that
information, but only if the source info has been loaded into the Lisp.
The source-debugger uses a lazy approach to load source debug info,
and
cl:function-lambda-expression
will not prompt to get file information if that information is
not already loaded in. If the fasl file that contains the source info
was loaded in while *load-source-file-info*
was true, then the
info will be present. Or, if any debugging commands have caused the
source info to be loaded, then it will already be available; the
easiest way to force this is to set a breakpoint somewhere in the
desired function. If source-file-recording was turned on when the
fasl file is loaded (that is, *record-source-file-info*
was true when the
file was compiled and *load-source-file-info*
was true when the file
was loaded), then the source-debug info will not normally be loaded,
but the Lisp will know where to get the information from and it will
happen automatically. (See also *load-source-debug-info*
.)
Often you wish to write floating point numbers to a file which is read later, perhaps in the same Lisp invocation but more likely in a different one. The simple way to do this, writing the decimal representation of the floats, suffers from being very inefficient and somewhat inexact. (It is expensive to convert the internal binary representation to decimal for writing, and then the decimal representation back to binary when the values are read.)
Allegro CL provides functions that write and read the binary representation of floats (rather than the decimal representation), thus saving the time and loss of accuracy associated with conversion to and from decimal format. The functions are single-float-to-shorts, double-float-to-shorts, shorts-to-single-float, and shorts-to-double-float. Note that machine binary representations are used by most languages, and so, just as Allegro CL can read the files produced by writing the integers returned by single-float-to-shorts and convert them back to floating point numbers, programs easily written in other languages can do so as well.
An additional version optional argument has been added to cl:provide and cl:require. It allows specifying a minimal version acceptable for loading a module.
Arguments: module-name &optional version
The non-standard version argument, if specified, should be a positive real number (a float or an integer). This value is checked when the module specified by module-name is loaded. If the cl:require form also specified a version, it is compared (numerically) with the version in the provide form. If the require version is less than the provide version, a continuable error is signaled. If either form does not have a version specified, there will be no error.
Arguments: module-name &optional pathname min-version
The non-standard min-version argument, if specified, should be a positive real number (a float or an integer). This value is compared with the version specified in the cl:provide form in the module. An error will be signaled if the cl:provide form has a version less than the value of min-version. If the cl:provide form has no version specified (or there is no cl:provide), no error will be signaled.
Both macroexpand and macroexpand-1 are enhanced to receive a new argument, special-operator-stop, and to add behavior for specific kinds of environments.
Arguments: form &optional environment special-operator-stop
A second (non-standard) optional argument, special-operator-stop, has been added to allow controlling behavior for specific kinds of environments.
If the environment passed in is a
:compiler
environment (as opposed to a
:compilation
, :interpreter
,
:evaluation
, or :macros-only
)
then compiler-macros will be expanded by both macroexpand and macroexpand-1. This is to simulate what
happens when the compiler does its macro expansions. Note that this
behavior is an extension to the ANSI Spec, which states that
compiler-macros are not expanded by
macroexpand/macroexpand-1. Note also that any portable
code-walker which expects to receive an ansi-compliant environment
must condition the environment by using sys:ensure-portable-walking-environment on
the argument.
If a :compiler
environment is passed in to macroexpand/macroexpand-1 and
special-operator-stop is true, then a
special-form (a form whose car is a special-operator) will not be
macroexpanded, as is otherwise usually the situation. This feature is
provided to allow a specialized code-walker (not necessarily a
portable one) to see what special forms the compiler sees. If the
walker then knows how to interpret the syntax of the special-operator,
it can do so in an implementation-dependent way; otherwise, it can
always do the macroexpansion again with the
special-operator-stop set to nil
, in order to get a full macroexpansion through
the special form.
cl-user(1): (setq env (sys:make-compilation-unit-environment)) #<Augmentable compiler environment @ #x40c916aa> cl-user(2): (macroexpand '(case num (1 (foo "one")) (2 (foo "two")) (3 (foo "three")) (otherwise (foo "unknown"))) ) (let () (cond ((eql '1 num) (foo "one")) ((eql '2 num) (foo "two")) ((eql '3 num) (foo "three")) (t (foo "unknown")))) t cl-user(3): (macroexpand '(case num (1 (foo "one")) (2 (foo "two")) (3 (foo "three")) (otherwise (foo "unknown"))) env) (let () (cond ((eql '1 num) (foo "one")) ((eql '2 num) (foo "two")) ((eql '3 num) (foo "three")) (t (foo "unknown")))) t cl-user(4): (macroexpand '(case num (1 (foo "one")) (2 (foo "two")) (3 (foo "three")) (otherwise (foo "unknown"))) env t) (excl::simple-case num (1 (foo "one")) (2 (foo "two")) (3 (foo "three")) (otherwise (foo "unknown"))) t cl-user(5): (macroexpand-1 * env t) (excl::simple-case num (1 (foo "one")) (2 (foo "two")) (3 (foo "three")) (otherwise (foo "unknown"))) nil cl-user(6): (macroexpand-1 * env) (let () (cond ((eql '1 num) (foo "one")) ((eql '2 num) (foo "two")) ((eql '3 num) (foo "three")) (t (foo "unknown")))) t cl-user(7):
Arguments: form &optional environment special-operator-stop
The effect of the second (non-standard) optional argument to macroexpand-1 is the same as described just above in the description of macroexpand in Allegro CL. See that description and the associated examples for further details.
The generic functions cl:simple-condition-format-control and cl:simple-condition-format-arguments take condition arguments and return the values of the respective slots. Allegro CL extends the condition system to define and sometimes bind the format-control and format-arguments slots in all conditions. That is, the slots always exist but are only sometimes bound. The slot names are internal in the excl package, and so are excl::format-control and excl::format-arguments.
Because the slots are not always bound (except for actual simple-conditions), code should check that there are bound before trying to access them, with tests like:
(slot-boundp instance 'excl::format-control) (slot-boundp instance 'excl::format-arguments)
Because these slots need not exist in Lisps other than Allegro CL (again except for simple-conditions) code which tries to access them should be conditionalized for Allegro CL.
Many conditions in Allegro CL which are not simple conditions bind
these slots (including, for example, undefined-function
and and unbound-variable
). We do
not give a list, however, because it will likely go out of date. Users
who wish to make use of the slot value should, again, test whether
they are bound before accessing them.
user-homedir-pathname is a Common Lisp function that "determines the pathname that corresponds to the user's home directory on host." host is an optional argument.
In Allegro CL, the host argument is ignored in all cases. Allegro CL simply polls the Operating system in which it is running asking for the current user's home directory. On UNIX, this concept is well defined. On Windows, the notion of a home directory is more murky. Here is what Allegro CL does on Windows.
If in step 1 or 2, an invalid pathname is constructed (typically a pathname naming a non-existent directory), a warning is signaled and the invalid pathname is not returned.
On Windows, you can change what user-homedir-pathname returns in a running Lisp by setting the values of the HOMEDRIVE and HOMEPATH environment variables to be strings which when concatenated name the desired existing directory, for example (here we leave HOMEDRIVE unchanged):
(setf (sys:getenv "HOMEPATH") "\\mydir\\")
Only the Lisp process see these new values. You are not setting them for all processes or permanently. See also username-to-home-directory.
The standard readtable, which is the initial value
of *readtable*
cannot be
modified. You can, of course, copy it and modify the copy as
desired.
The restriction on modifying the standard readtable can affect user code in the following cases:
*readtable*
,
to their initial or default values. An attempt in the body of a call
to with-standard-io-syntax to
modify the current readtable (the value of *readtable*
) will fail unless the value of
*readtable*
has been changed earlier in the body to
the macro to a non-read-only readtable.
*readtable*
during initialization is
the initial readtable and it is read-only. So if you have code which
tries to modify it, that code will error. You must create a copy of
the initial readtable and then modify that and use it elsewhere. We
discuss this in more detail below.
Suppose you want to make the { character into a reader macro which extracts a specified element from a list (this is the example in the ANS description of set-dispatch-macro-character), and you put the following form into your .clinit.cl file or with the -e command-line argument (see Initialization and the sys:siteinit.cl and [.]clinit.cl files and Command line arguments in startup.htm):
(set-dispatch-macro-character #\# #\{ ;dispatch on #{ #'(lambda(s c n) (let ((list (read s nil (values) t))) ;list is object after #n{ (when (consp list) ;return nth element of list (unless (and n (> 0 n (length list))) (setq n 0)) (setq list (nth n list))) list)))
Since the optional readtable argument is not specified, it defaults to
*readtable*
,
but while the init file is being processed, the value
of *readtable*
is the initial readtable and that is read-only, so that form will
signal an error. What you must do is create your own readtable and
modify it:
(defvar *my-rt* (copy-readtable nil)) (setq *readtable* *my-rt*)
Now the form will work, modifying the reatable which is the value of
*my-rt*
and (for now) the value of *readtable*.
The same forms work when evaluated with the -e command-line argument (see Command line arguments in startup.htm).
But when the listener starts up, it will bind *readtable*
to a value specified in the
association list which is the value of *cl-default-special-bindings*
(see Setting
global variables in initialization files
in startup.htm). The entry
for *readtable*
is initially (*readtable* copy-readtable nil)
,
which means the listener will see a copy of the default readtable, not
your modified one. (In earlier releases, you modified the default
readtable so the changes were propagated.)
You modify *required-top-level-bindings*
with a form like
the following which you put below the forms above in your init file or
as a later form passed with -e:
(setf (third (assoc '*readtable* *required-top-level-bindings*)) '*my-rt*)
If you specify an improper value as the value of
the end keyword argument to sequence functions
(or the end1 or
end2 arguments), no checking is done and you may
get an error or an incorrect result. In this example, you get an
incorrect result (the third returned value should also
be nil
because the effective end of array is
0, but end2 is specified as 3 so that value is
used):
cl-user(3): (let ((*print-array* t) (array (make-array 0 :fill-pointer 0 :adjustable t))) (vector-push-extend :a array) (vector-push-extend :b array) (vector-push-extend :c array) (vector-push-extend :d array) (setf (fill-pointer array) 0) (values array (search '(:b :c) array) (search '(:b :c) array :end2 3))) #() nil 1 cl-user(4):
This lack of checking is permitted by the ANS. Actually checking every case would burden legal code to protect against erroneous code. Users should check themselves if this is an issue.
While investigating ways to speed up Allegro CL, developers at Franz determined that pretty printing was a significant user of compute cycles, and that turning pretty printing off produced significant speedup of code that did output. This conclusion is not particularly suprising, of course. It takes work to produce pretty output. The question is, what to do about it. Turning off pretty printing sounds easier than it is.
Allegro CL starts with *print-pretty*
set to
t
and further, the value in *cl-default-special-bindings*
is (essentially) t
as well. So simply setting
*print-pretty*
to nil
will
not work because the true value will tend to return unexpectedly (in
new processes, for example).
Further, user code may depend on the initial value of
*print-pretty*
being t
, so
the initial value could not be changed.
However, we can make suggestions to users so that they can achieve the speedups when desired.
There are three steps.
(1) change the value of *print-pretty*
in *cl-default-special-bindings*
to nil
by evaluating
(setq *print-pretty* nil) (tpl:setq-default *print-pretty* nil)
(See setq-default.)
(2) avoid using format strings that are pretty-printing by nature
(such as ~< ... ~:$gt;
).
(3) Set the value of *pprint-gravity*
to
nil
. Code in Allegro CL that used to bind
*print-pretty*
to t
now
bind it to *pprint-gravity*
. That variable is
not set on the *cl-default-special-bindings*
list.
There is a module pprint.fasl. When loaded into
an image, it sets *print-pretty*
and
*pprint-gravity*
to t
. This module is loaded automatically when an image
is built with a standard top-level. However, when an image is built
with a minimal top-level (as described in Minimal top
levels in building-images.htm, the
pprint
module is not loaded.
So when building an image, you can include them for a development image (above), putting them in, say, custom.cl, or you can build the image with a minimal top-level.
Note that the guts of pretty-printing are in the
pprint
module. Whenever a format statement, or a
print statement with *print-pretty*
set to t
, is executed, the pprint module is required, so
that the machinery is present to do the pretty-printing. So you have
to be careful to avoid such cases. If you need pretty printing, you
should use the strategy presented above rather than a minimal
top-level strategy.
Starting in release 6.2, the new variable *print-circle-gravity*
acts
with respect to *print-circle*
as *pprint-gravity*
does with *print-pretty*
: no Allegro CL
code sets the value of *print-circle*
. Instead, Allegro CL code binds
it where necessary to the value of *print-circle-gravity*
, and *print-circle-gravity*
is only
set in two places: initially to nil
and in
the :pprint
module to t
.
Please check the Allegro CL FAQ from time to time to see if there is new information on this issue.
The class-precedence-list is calculated by mop:finalize-inheritance but it is not installed into the class until close to the end of finalization, as mop:class-precedece-list signals a program error when the class is not finalized. But the class-precedence-list is available much earlier and can be accessed with
(slot-value class 'mop:class-precedence-list)
after it is actually calculated (but before the operator mop:class-precedece-list can access it).
The IEEE floating-point standard calls for infinities and NaNs (Not-a-Number) to be represented and used. So division of a non-zero finite float by zero produces an infinity, while a division of zero by zero produces a NaN.
the Common Lisp standard does not call for these special floats (as they are often called), but does allow for implementation of IEEE. Further, compiled code which dispatches directly to an IEEE floating-point processor may get back a special float result which it may just return, particularly if it is compiled at high speed and low safety. So consider the following from Allegro CL:
cl-user(115): (defun foo-err (sf) (declare (single-float sf)) (declare (optimize (speed 1) (safety 1))) (/ 1.0 sf)) foo-err cl-user(116): (compile *) foo-err nil nil cl-user(117): (foo-err 0.0) Error: Attempt to divide 1.0 by zero. [condition type: division-by-zero] [1] cl-user(118): :reset cl-user(119): (defun foo-inf (sf) (declare (single-float sf)) (declare (optimize (speed 3) (safety 1))) (/ 1.0 sf)) foo-inf cl-user(120): (compile *) foo-inf nil nil cl-user(121): (foo-inf 0.0) #.excl::*infinity-single* cl-user(122):
In the safe code, we get an error. In the fast code, we (silently) get infinity. This is not unexpected. Fast code is supposed to be fast and it achieves speed by discarding checks. The fp processor on the machine where this was run is an IEEE processor and it does not set the error flag for division by zero. Instead, it retuns the legal IEEE float infinity.
There are predicate function that return true when an object is a floating-point infinity or a NaN. exceptional-floating-point-number-p returns true when passed an floating-point infinity or NaN. nanp returns true when passed a NaN and infinityp returns true when passed an infinity.
Because underflows and overflows often signal an error, code like
(expt most-positive-single-float 2)
will now error
rather than returning an infitity. Users who want infinite value
and who do something like:
(defvar +single-positive-infinity+ (expt most-positive-single-float 2))
should instead do
(defvar +single-positive-infinity+ excl:*infinity-single*)
The six special-float constants are:
*infinity-single*
*infinity-double*
*negative-infinity-single*
*negative-infinity-double*
*nan-single*
*nan-double*
Arithmetic operations with special floats are legal. Generally, the result of an operation with a special float is a special float, the exception being dividing by infinity which produces zero. The following table shows the result of operations. Note we do not cover all cases nor most floating-point coercion cases. An operation with a double and a single results in a double.
Operation |
Arg1 |
Arg2 |
result |
Any |
*nan-single* | Any single | *nan-single* |
Any |
*nan-single* | Any double | *nan-double* |
Any |
*nan-double* | Any | *nan-double* |
+ |
*infinity-single* | finite single-float | *infinity-single* |
+ |
*infinity-single* | finite double-float | *infinity-double* |
+ |
*infinity-double* | Any finite | *infinity-double* |
+ |
*infinity-single* | *negative-infinity-single* | *nan-single* |
- |
Any finite single-float | *infinity-single* | *negative-infinity-single* |
* |
*infinity-single* | Any positive single-float | *infinity-single* |
* |
*infinity-single* | Any negative single-float | *negative-infinity-single* |
* |
*infinity-single* | 0.0S0 | *nan-single* |
/ |
*infinity-single* | *infinity-single* | *nan-single* |
/ |
*infinity-single* | any positive finite single-float | *infinity-single* |
/ |
any finite single-float | *infinity-single* | 0.0S0 |
/ |
0.0S0 | *infinity-single* | 0.0S0 |
/ |
*infinity-single* | 0.0S0 | *infinity-single* |
/ |
Any positive single-float | 0.0S0 | *infinity-single* |
/ |
0.0S0 | 0.0S0 | *nan-single* |
Actually, we just discuss underflow errors. The same method will work
with overflow errors. Before handling the error, you must decide what
you want to do: for an underflow, do you want to return zero? Or (if
sorking in single-floats), switch to doubles and try again? Or return
least-positive-single-float
or least-positive-single-float
(assuming positives)? See also the function read-tiny-float which handles the underflow
error when reading a single float from a string, giving you the option
of returning zero or the least-positive/least-negative float of the
appropriate format.
Once you have decided that, code like the following will handle the error:
;; This read-from-string form will signal an underflow error: cl-user(1): (read-from-string "4.9e-324") Error: expt operation on (2.0 -723) resulted in floating point underflow. [condition type: floating-point-underflow] ;; This condition code will trap the error and return 0.0: (catch 'trap-error (handler-bind ((floating-point-underflow #'(lambda (c) (declare (ignore c)) (throw 'trap-error 0.0)))) (read-from-string "4.9e-324")))
That condition code can be adapted to other underflows and also
overflows (changing the condition type to
floating-point-overflow
).
Because the natural word size on 32-but machine is 32-bits and on
64-bit machines is 64-bit, we have defined new
types nat
and unsigned-nat
which
is a 32-bit integer on 32-bit machines and a 64-bit integer on 64-bit
machines. One can use nat
and unsigned-nat
in places
where int
and unsigned-int
(and
sometimes, long
and unsigned-long
) normally go. This allows
sources for both 32-bit and 64-bit lisps to be the same.
Functions that accept nat and unsigned-nat as values for arguments include memref and memref-int and stack-cushion and set-stack-cushion. See also ftype.htm (particularly The Syntax for Foreign Types) and lisp.h.
The #A reader macro is a standard Common Lisp reader macro, document here is the ANS. Allegro CL extends #A as we describe next. The usage template is:
#{n}A{t}data ;; The braces -- {} -- indicate the value is optional ;; data must be a sequence.
The standard CL usage template is #nAdata
. Allegro
CL makes the n parameter optional (it defaults to 1). The new optional
parameter t
can be used to specify the element-type
and can be one of u4, u8, u16, u32, u64 s8, s16, s32, or
s64. s
indicates signed-byte, u
unsigned-byte. the integer represents the number of bits per byte. So,
for example, #Au8(1 2 3 4)
reads as an
(unsigned-byte 8), 1-dimensional array (that is, a vector) with 4
elements, 1, 2, 3, and 4.
The reader will always read the new format correctly, but the printer
will only use the new format when the variable *print-simple-array-specialized*
is true. The
new format is, of course, not portable to other Common Lisp
implementations. When that variable is nil, the standard #nAdata
format is printed.
When n
is 0, it is assumed
that t
is not provided, so #0Au8123 produces a
0-dimensional array of type t
(not type
(unsigned-byte 8)).
Allegro CL has a number of printer variables which control the length
and level of particular kinds of
printing. Thus, tpl:*print-length*
controls the print-length
used when printing return values at the
top-level. cl:*print-length*
controls the print-length
used by the various print functions. This example illustrates the
difference. Recall print
returns its argument after printing it:
(setq *print-length* nil) (setq tpl:*print-length* 5) cl-user(16): (print '(1 2 3 4 5 6 7 8 9 10)) (1 2 3 4 5 6 7 8 9 10) (1 2 3 4 5 ...) cl-user(17):
The Allegro CL print variables can have the same values as the CL ones
(an integer or nil
). They may also have the
value :follow
. That value means use the value of
the corresponding CL variable. So if we
make :follow
the value of tpl:*print-length*
, we get this behavior:
cl-user(9): (setq *print-length* 5) 5 cl-user(10): (setq tpl:*print-length* :follow) :follow cl-user(11): (print '(1 2 3 4 5 6 7 8 9 10)) (1 2 3 4 5 ...) (1 2 3 4 5 ...) cl-user(12): (setq *print-length* nil) nil cl-user(13): (print '(1 2 3 4 5 6 7 8 9 10)) (1 2 3 4 5 6 7 8 9 10) (1 2 3 4 5 6 7 8 9 10) cl-user(14):
This facility should be used with care, especially with the tracing
print variables (*trace-print-level*
etc.), since it eliminates
the protection those variables provide by default when huge or
circular objects are encountered during tracing, or contrarily, it may
prevent trace from providing useful output. A traced function may be
called in a dynamic environment where some surrounding code has for
its own legitimate purposes bound some of these variables to extreme
values.
The Allegro CL print variables include:
*step-print-length*
*step-print-level*
*trace-print-array*
*trace-print-circle*
*trace-print-length*
*trace-print-level*
tpl:*zoom-print-length*
tpl:*zoom-print-level*
tpl:*zoom-print-circle*
tpl:*print-length*
tpl:*print-level*
Most platforms support 32-bit and 64-bit Lisps. (See Installation sizes and supported Operating System versions in installation.htm for a list of supported platforms and Lisps.)
For the most part there is no compatibility issue, especially when dealing with Lisp. The exceptions are:
-DAcl32Bit
or -DAcl64Bit
on the
cc line of any program which includes it, for any architectures for
which there are both 32-bit ports and 64-bit ports (Sun,
AIX, for example). It is a good idea to add the -DAcl32Bit option to cc
lines to be sure as additional platforms may be added later.
:int
, :long
,
:unsigned-int
, and
:unsigned-long
types always follow precisely the
sizes of the foreign modules that are successfully loaded into that
Lisp.
Again, most pure-lisp behavior will be completely portable between 32-bit and 64-bit lisps, and that at most a user is likely only to see wider values while inspecting objects, or larger addresses in room displays.
For operations that must deal with specific sizes but do not use the def-foreign-type interface, the natural type (in Lisp) and the nat type (in C) provides a simple method to allow compatibility between 32-bit and 64-bit lisp code and foreign modules.
Allegro CL is an implementation of Common Lisp as specified by the ANSI X3J13 committee. The standard of conformance has been accepted by ANSI as final. ANSI is the American National Standards Institute, and the X3J13 committee prepared the ANSI standard for Common Lisp.
Common Lisp was originally specified in Common Lisp: the Language, 1st edition (CLtL-1). That standard is now out of date. Common Lisp: the Language, 2nd edition (CLtL-2) describes an early version of the ANSI standard. It is still used but please understand that the final ANSI standard has diverged in a number of ways from CLtL-2, so CLtL-2 is no longer definitive.
Loading the cltl-1 module may affect ANSI compliance. Note compiler-let is available and exported from the excl package.
The several symbols removed from the language by X3J13 but preserved
by Allegro CL for backward compatibility are exported from the
cltl1
package. These generally retain their CLtL-1
definitions. We list the symbols exported from the
cltl1
package at the end of this section. Note that
the definitions are in the :cltl1
module.
Two symbols exported from the flavors
package conflict
with symbols now exported from the common-lisp package as part of
CLOS: defmethod and make-instance. This means that no
package can use the flavors package without shadowing these two
symbols. See the code at the beginning of
flavors.htm.
The following symbols in the cltl1 package have been deleted from standard Common Lisp by X3J13. They (for the most part) maintain their CLtL-1 functionality. You may use the cltl1 package to get backward compatibility but we recommend that you write all new code so that you do not use these symbols and that you modify all existing code as soon as practical.
Note that special-form-p is in the cltl1 package. That symbol was previously in the common-lisp package but has been replaced in that package with the symbol special-operator-p.
These symbols were in the cltl1
package since
release 4.0 and are still there:
*applyhook*
: (variable) along with applyhook,
*evalhook*, and evalhook, provided functionality to affect
evaluation.
*break-on-warnings*
: (variable) if true, go into a
break loop when a warning is signaled (essentially replaced by *break-on-signals*
).
*evalhook*
: (variable) along with applyhook,
*applyhook*, and evalhook, provided functionality to affect
evaluation.
string-char
: (class) the type of elements that
could be in a string. Essentially, in ANSI CL, strings contain
characters.
string-char
(see just above).
X3J13 made a number of improvements to the package system in order to facilitate portability and to regularize the handling of top-level forms in a file. The function in-package was changed to a macro, and its various keyword arguments were deleted. The macro expansion of in-package is defined to have effect at compile, load, and eval times, but no longer creates a package if it does not exist, nor modifies any existing package. These functionalities are subsumed by the new defpackage macro, along with that of the several other package-manipulating functions. The package name argument to in-package is no longer evaluated. Execution of an in-package form referencing an unknown package or containing optional arguments signals a continuable error.
The variable *cltl1-in-package-compatibility-p*
makes
in-package work as it did in CLtL-1 Common Lisp. Users porting
code from Allegro CL for Windows 3.0.x (which used CLtL-1 semantics in
this regard) may find this variable useful. We do recommend modifying
the code in the long run, however.
By compile-time-too behavior, we refer to the effect of certain top-level forms in a file being compiled. In CLtL-1, top-level forms which were calling the functions listed below were treated as if they were wrapped in an
(eval-when (compile))
form. That behavior has been changed in the new standard and you must wrap such forms in appropriate eval-when forms if they are to have effect while a file is being compiled. The affected functions are:
proclaim make-package shadow shadowing-import import export unexport use-package unuse-package require
The variable *cltl1-compile-file-toplevel-compatibility-p*
can be used to get CLtL-1 compile-time-too behavior when compiling
files. Users porting code from Allegro CL for Windows 3.0.x (which
used CLtL-1 semantics in this regard) may find this variable
useful. We do recommend modifying the code in the long run,
however.
X3J13 tightened the definition of the function data type, primarily
so generic functions could discriminate on functional arguments. It
was necessary that the type represented by the function datatype and
functionp predicate be disjoint from all other datatypes. Therefore,
in Allegro CL since version 4.2 the only objects that are type
function are those returned by the function special form, or by the
compile function given a first argument of nil
, or by coerce of a lambda expression to type
function, or functions loaded from a compiled file. X3J13 specifies
that the funcall and apply functions will continue to accept a
symbol for the first argument, but a symbol is no longer
functionp, nor are lists beginning with
lambda
, sometimes called lambda expressions. For
backward compatibility the funcall and apply functions
in Allegro CL will still accept a lambda expression, as is permitted
by X3J13, but as required by X3J13 lambda expressions no longer
satisfy functionp nor (typep function)
.
Previous versions of Allegro CL have used Portable Common Loops (PCL) as a substitute for the Common Lisp Object System (CLOS) which was adopted by X3J13 as a standard part of Common Lisp. The last several versions of PCL worked in most ways the same as CLOS and provided most of the required features. (Some unavoidable divergences of PCL from CLOS derived from the dependence of CLOS on certain other incompatible language changes.)
Since CLOS replaces PCL completely, there has been no attempt to port any version of PCL to Allegro CL since prior to release 4.3. Doing such a port would be difficult, and would not benefit from the significant speed advantages of the native CLOS implementation in Allegro CL. User code that depends on various details of PCL (especially internals) may have temporary difficulties, but in any case such code will someday need to be brought into conformance with CLOS. In addition to full conformance with CLOS, of course, the other advantage of the native CLOS implementation is its greatly enhanced runtime performance.
CLOS is documented in chapter 28 of CLtL-2. MOP is documented in the book The Art of MetaObject Protocol.
It is possible to trace, disassemble, and compile CLOS methods by name. Here is an example of tracing.
USER(14): (defmethod my-function ((x integer)) (cons x :integer)) #<clos:standard-method my-function ...> USER(15): (my-function 1) (1 . :integer) USER(16): (trace ((method my-function (integer)))) ((method my-function (integer))) USER(17): (my-function 1) 0: ((method my-function (integer)) 1) 0: returned (1 . :integer) (1 . :integer) USER(18): (untrace (method my-function (integer))) ((method my-function (integer))) USER(19): (my-function 1) (1 . :integer) USER(20):
Here is how to trace setf,
:before
, and :after
methods (the
names and argument types will likely be different in your case, of
course):
(trace ((method (setf slot-1) (t baz)))) (trace ((method foo :before (integer)))) (trace ((method foo :after (integer))))
The extra set of parentheses is required to avoid confusion with specifying trace options (they are specified with a list whose car is the function to be traced and whose cdr is a possibly empty list of options). Note that the extra set of parentheses is not used with untrace:
(untrace (method (setf slot-1) (t baz))) (untrace (method foo :before (integer))) (untrace (method foo :after (integer)))
A generic function itself can be traced exactly like any other function.
We list known non-conformances with CLOS and MOP. The basic format is to list the object that is unimplemented or only partially implemented with a brief description of the non-conformance. Unqualified symbols are part of CLOS and are exported from the common-lisp package. Symbols qualified with clos: are part of MOP (they are exported from the clos package).
[Generic function] clos:class-prototype
Implemented for clos::std-class only. clos::std-class (which is not part of the CLOS standard) is a superclass of funcallable-standard-class and standard-class but is not a superclass of forward-referenced-class, structure-class, and built-in-class. Therefore, methods are defined on the first two classes but not the next three. (This is not actually a non-conformance.)
[Special form] generic-flet
Removed from spec by X3J13 and not implemented.
[Macro]
generic-function
Removed from spec by X3J13 and not implemented.
[Special form] generic-labels
Removed from spec by X3J13 and not implemented.
[Generic function]
clos:make-method-lambda
Not implemented.
[Special form] with-added-methods
Removed from spec by X3J13 and not implemented.
Calls to make-instance where the class-name is a quoted constant and each of the keywords is a constant are transformed by the compiler into calls to constructor functions. A constructor function is a piece of code that is equivalent to the make-instance call except that it is significantly (10 to 100 times) faster.
The optimization is automatic when the call to make-instance is formed in a particular way. In order for an optimized constructor function to be used certain restrictions apply:
Generic function |
Condition for optimization |
make-instance | Only system supplied methods are applicable |
initialize-instance | Only system supplied standard method and user-supplied :after
methods are applicable |
shared-initialize | Only system supplied standard method and user-supplied :after
methods are applicable |
The calls to make-instance are replaced by calls to the constructor regardless of whether an optimized constructor can be used. The first time the constructor function is called, the restrictions are tested and if they do not apply, an optimized constructor is generated. When the restrictions are not obeyed the constructor calls make-instance. Redefining a class or one of its superclasses or adding/removing a method to one of the generic functions mentioned above causes the constructor function to be recomputed.
Function specs name and possibly locate functions not named or located
by symbols. Generally, a function spec is used and presented in the
same way as a symbol, although there are sometimes restrictions. Some
such functions like (setf foo)
can be located based
on the name, via (fdefinition '(setf foo))
or #'(setf foo)
, while others are intended only to
identify the function in a meaningful way, as
in (:top-level-form "bar.cl" 235)
. The extent of
the action that can be performed on a function spec depends on its
function spec handler, see def-function-spec-handler.
A function spec (fspec) is a list that denotes a place to store a
function. Function specs are useful for functions that don't otherwise
have obvious names. ANSI CL defines Function Names as either symbols
or the lists formed by (setf
[symbol])
[to denote a writer function to
pair with the reader function named by [symbol] which
may or may not itself be defined]. Allegro CL extends the Function
Name concept by defining function specs, and allows the user to create
new kinds of function specs. Some pre-defined function spec names in
Allegro CL are :discriminator
,
:effective-method
, method
,
flet
, labels
,
:internal
, :top-level-form
,
etc.
Function specs are normally kept in an internal form, which allows many of the cons cells in various fspecs to be shared. They are converted to the normal external format usually only when printing, or at other times when parsing the internal form is too complex. Handlers of fspecs must be aware of these internal formats and may use the following functions to access their components: fspec-first, fspec-second, fspec-third.
Each of these functions will work on either an internal or external fspec, and will for an external fspec return the first, second, or third element, respectively (i.e. just like first, second, and third). If the fspec is in internal form, the proper corresponding element is still returned, but without the overhead of first converting to an external fspec.
Users can define their own function specs with def-function-spec-handler. The function function-name-p returns true when passed a valid function spec defined with def-function-spec-handler. fboundp can be used to determine whether a valid function spec defined with def-function-spec-handler actually names an operator.
The following function specs are supported in Allegro CL. Note that some might only be recognized as function-specs when their appropriate modules are loaded. In all descriptions, the Available line describes when the function-spec is valid. The Elements line names the fspec-first element of the fspec, as well as fspec-second and, if applicable, fspec-third and elements beyond three.
Available: When compiler is present
Elements: (:anonymous-lambda index)
Names a compiled function that has not been given any other name. The index is an integer that is incremented for every new anonymous function created. This function-spec is not intended for defining or manipulation; it is recognized as a function name, but if for example the evaluation of
(compile nil (lambda (x) (1- x)))
returns#<Function (:ANONYMOUS-LAMBDA 2) @ #x406eae5a>
then(fdefinition '(:ANONYMOUS-LAMBDA 2))
will still result in an undefined function error. These names are not stored and not retrievable. They are also not recommended - instead, when compiling an anonymous lambda, it is easier to work with when given a name:CL-USER(10): (compile 'foobar (lambda (x) (1- x))) FOOBAR NIL NIL CL-USER(11): #'foobar #<Function FOOBAR> CL-USER(12):
Available: always
Elements: (:discriminator type)
Names any of the various discriminator functions, which memoize and select effective methods for particular calls. The type is a list whose car is the name of the kind of discriminator, and whose rest will have other parameterizations based on the discriminator kind. The various kinds of discriminators are listed below with their parameterizations and a short description. The parameterizations themselves are:
- class-slot-p: when true, the specializer is a class slot, otherwise, an instance slot
- metatypes: a list of items which correspond to arguments in the call; if the metatype is T then no discrimination is done; if the metatype is CLASS then the argument is specialized on that corresponding class.
- applyp: true if the generic function takes &rest args, otherwise nil.
Here are the actual discriminators:
(:one-index-reader class-slot-p) (:one-index-writer class-slot-p)For slot accessors, if the index is the same for every call (even though the specialized classes might be different) then the index, which is remembered by the discriminator, can simply be used to grab the slot value out of the instance based on the single index encountered. If the discriminator ever encounters a specialization whose slot is at a different index, the discriminator is replaced with the checking version.
(:few-class-reader class-slot-p) (:few-class-writer class-slot-p)If this discriminator's slot accessor has only ever been called with only one or two classes, both are noted and the appropriate one is used. If the call uses a third class, the discriminator is replaced with the n-n version.
(:n-n-reader) (:n-n-writer)For this discriminator's slot accessor, the specializer class and its index are stored in pairs into a cache, and looked up in subsequent calls.
(:checking metatypes applyp)This discrimator's specializers can be numerous, as long as they all result in the same method being called. That method is stored once, and all sets of specializers, one set per unique call, and if the specialiers match, the stored method is used. If a differnt method is required, the discriminator is replaced with the equivalent caching version.
(:caching metatypes applyp)This discriminator's specializers determine what method is to be called; if the set matches a set stored into the cache, the method becomes immediately available. If the specializers haven't been seen yet, the method is calculated and stored into the cache beore being called.
Available: always
Elements: (:effective-method num-req restp kwdcheck aroundp next-method-p)
Names a simple effective method function object, which is a closure over the applicable methods for a particular call to a generic function.
- num-req - the number of required arguments to the effective method
- restp - whether there is a &rest arg after the required args
- kwdcheck - whether the method will check for valid keywords
- aroundp - whether the effective method has around methods
- next-method-p - whether any of the methods in the effective method do either (next-method-p) or (call-next-method)
Note that kwdcheck implies restp, and aroundp implies next-method-p so the valid combinations are
restp kwdcheck aroundp next-method-p ----- -------- ------- ------------- nil nil nil nil nil nil nil t nil nil t t t nil nil nil t nil nil t t nil t t t t nil nil t t nil t t t t t
Available: always
Elements: (:internal outer-name index)
Provides a way of numbering compiled internal functions which serve the functions they are lodged within. The index is numbered in the order these functions are encountered within the compiler, so there is a loose top-to-bottom organization (e.g. #'(internal foo 0) is likely to textually preceed #'(:internal foo 1), etc). The outer-name is the name of the function which houses the internal function. It can itself be any fspec, including an :internal fspec, so :interal fspecs can be nested. :internal fspecs can't be used in defun, but can be found by fdefinition, as long as the outer-name can also be found by fdefinition.
Available: always
Elements: (:property symbol indicator)
Creates a convenient way to define functions which reside on property lists. The function object becomes the value of the indicator property on the symbol's plist. Example: typing
(defun (:property foo bar) (x) (list x))at the toplevel will result in the symbol 'foo having a 'bar property which is a function (and that functions name is '(:property foo bar)).
Available: always
Elements: (:top-level-form filename file-character-position)
Provides naming for random forms at the top level in a fasl file which refers back to its original source file. The file element is a string specifying the filename, and the position is the character count of the opening parenthesis of the form. The file-character-position is determined assuming the first character in the file has character position 0. See file-character-position.
Notes:
- Since progn forms are compiled as if separate forms, but they are all in fact part of the same form, there can be many function objects with the same :top-level-form fspec. Also, since a defmacro can expand into a progn form, a macro form might also result in numerous funtions with the same :top-level-form fspec, as well as other more standard fspecs such as those created by defun forms embedded in the macroexpansion.
- The position of :top-level-form fspecs must be adjusted on Windows, or in any situation where "CRLF" processing or any other ligature processing or stream encapsulation would cause the number of actual characters the lisp sees to be altered from the number of characters actually in the file. Usually, if the editor doesn't support counting the CR/LF line endings as two characters, the position in a Windows file can be "fixed" or at least mitigated by adding the current line number to the position. If that results in more lines being traversed, those lines should be added as well.
Available: When the Runtime Alalyzer is loaded (see runtime-analyzer.htm), and when a profile exists that has been created with the :interpret-closures option to with-profiling or start-profiler.
Elements: (prof:closure index)
When :interpret-closures is true during the creation of a profile, this function spec allows the user to see closure calls that otherwise are unrelated as separate hits in the profiling run. The flat-profile and the call-graph will both show closures as (prof:closure index) rather than by trying to interpret the function name of the template, which is sometimes not helpful. See Closures in runtime analysis output in runtime-analyzer.htm.
Elements: (compiler-macro function-spec)
Allows easy access to compiler macros as fspecs, rather than using the compiler-macro-function accessor. Function-spec is the same as the quoted function-spec required as an argument to compiler-macro-function.
Example:
(define-compiler-macro (setf foo) (x y) (setf x y))will define a compiler-macro for
(setf foo)
, and subsequently evaluating any of#'(compiler-macro (setf foo))
or(fdefinition '(compiler-macro (setf foo)))
or (compiler-macro-function '(setf foo)) will yield the compiler-macro-function just created.
Available: always
Elements: (flet outer-name inner-name)
Provides a way to directly name compiled internal flet functions, which are of course always lodged within other functions. The inner-name is the function-name element of the flet form, which serves lexically within the outer funcion as the identifier. The outer-name is the name of the function which houses the flet function. It can itself be any fspec. This fspec can't be used in defun, but can be used in fdefinition (as long as the flt and its outer-name function are compiled).
Available: always
Elements: (labels outer-name inner-name)
Like flet, only a labels function is described.
Available: always
Elements: (method name [qualifiers] specializers)
Allows a CLOS method to be named directly. Name is the name of the generic-function of the method. There can be multiple qualifiers which determine and are determined by the method combination type. Specializers is a list that corresponds to the names of class specializatoins for each argument in the method; if the argument is specialized, the specializer is the name of the class; if the argument is not specialized, the specializer is T. The method fspec cannot be used in defun, but can be used in fdefinition.
Available: always
Elements: (setf reader)
This is the only standard fspec. It defines the name of the setf version of the (possibly non-existent) reader function. This fspec can be used in defun or fdefinition.
The C function GetWinMainArgs2 in the Allegro CL dll (which has a name like acly7xxx.dll, where the y, if present, is a letter and the x's are digits) is used by the IDE to retrieve Windows handle information known by the ACL runtime system. This information may also be useful for applications written in Lisp. The information returned by this function should be used carefully and non-destructively as the ACL runtime system (i.e. the low-level routines in Allegro CL, unrelated to Allegro Runtime) depends on these handles to exist and behave in predictable ways.
In order to use this function you must use the foreign function interface to create a Lisp function to call the C function:
(ff:def-foreign-call (GetWinMainArgs2 "GetWinMainArgs2") ((vec (:array :int)) (count :int)) :returning :void)
Next create a vector of five raw integers for the function to fill in:
(setq myvec (make-array 5 :element-type '(unsigned-byte 32)))
Now call the function with the vector followed by the number of elements in the vector
(GetWinMainArgs2 myvec (length myvec))
The vector now contains the following information
index value 0: The Windows instance handle of the lisp process
index value 1: The previous Windows instance handle (which is always zero).
index value 2: unused
index value 3: The Windows handle of the console window (if there is one).
index value 4: The Windows handle of the splash window. Normally the splash window is gone by the time the application starts up, but the +B command-line argument to mlisp.exe can cause the splash window to stay up longer. If this value is non-zero then the application is permitted to call the Windows function DestroyWindow() on it to make the splash window disappear. If this value is zero then the splash window is already gone.
The following list describes the (mostly minor or obscure) known non-conformances of Allegro CL with the ANSI spec.
nil
when given a non-existent file as an
argument (rather than signaling an error), which is arguably a
non-conformance. See the discussion at
Section 6.14 cl:file-write-date. There is no plan
to change this behavior.
declared-fixnums-remain-fixnums-switch
, if
true, allows code to be generated by the compiler which will produce
the incorrect value when certain fixnums (those whose sum is a bignum)
are added. Allowing incorrect values from fixnum addition under any
circumstances is out of compliance with the ANSI specification. This
compiler switch may be set to nil (thus removing any non-compliance)
by evaluating (setf
comp:declared-fixnums-remain-fixnums-switch nil)
.
nil
.
*pathname-customary-case*
.
nil
signals an error, and it should not.
call-next-method
with changed
arguments does not check that the set of applicable methods has not
changed, though it should in safe (safety=3) code.
the
clause. The expansion should not literally
include the the
form, but the effect of the type
declaration may be effectuated by the compiler by some other
means. Allegro CL does not conform, and the expansion of a symbol-macrolet variable with a type
declaration includes a the
form.
nil
for purposes of type checking. In Allegro CL in
interpreted code, an error is signalled unless a the form agrees exactly with both the number and
types of the returned values. However, in compiled code, it does not
check values returned through a the form (although the type
declaration may be used for code optimization) and therefore complies.
Copyright (c) 1998-2019, Franz Inc. Oakland, CA., USA. All rights reserved.
This page has had moderate revisions compared to the 8.2 page.
Created 2019.8.20.
| Allegro CL version 9.0 Moderately revised from 8.2. 8.2 version |