Category Archives: Uncategorized

The less-familiar parts of Lisp for beginners — with-compilation-unit

This is certainly an infrequently encountered macro, but the example and description in the CLHS are quite helpful.  The macro is used to defer warnings until multiple compilation units can be processed, to avoid spurious warnings, for instance when there two compilation units call each other’s functions.

If you, like me, try hard to eliminate all warnings during compilation, this macro can be useful.  Warnings during compilation can often indicate real problems, but if your code in its normal state is generating pages and pages of them, a new warning might be easy to miss, so anything that eliminates spurious warning messages is helpful.

The less-familiar parts of Lisp for beginners — with-accessors

The with-accessors macro is a syntactic shortcut that simplifies forms that manipulate the slots of an instance.  It creates a shorthand expression in a new lexical environment that replaces an accessor form acting on an instance with a simple symbol.  This symbol is setf-able, it is not a local variable that is filled in with the value of the slot.  In C++ parlance, this macro creates references to members within an object.

There is a bit of a tradeoff between convenience and clarity here.  While the macro allows the programmer to avoid typing a lot of accessor forms, the symbols created are not, at a glance, obviously manipulating an instance, which can lead to a bit of confusion when reading the code.  Of course, it comes down to a personal preference, there’s nothing you can do with with-accessors that you cannot easily achieve with more familiar operations.

The less-familiar parts of Lisp for beginners — vector-pop, vector-push, vector-push-extend

The newcomer to Lisp has probably already seen the push and pop operations, that allow the programmer to insert and remove elements at the beginning of a list.  There are also vector equivalents of these, vector-push and vector-pop.  These operations apply to a vector with a fill pointer, and for efficiency reasons act on the last element in the vector, rather than the first.  Note, though, that there are some caveats to be aware of when using vector-push.

Recall that a vector with a fill pointer has a “true” length, and an effective length governed by the fill pointer.  The vector-push operation inserts just past the last element, as determined by the fill pointer, and increments the fill pointer, returning the new value of the fill pointer.  However, if that operation would cause the fill pointer to exceed the actual length of the vector itself, vector-push does nothing and returns nil.  To simplify handling of this case, there is another function, vector-push-extend, that acts just like vector-push, but also increases the length of the vector if that would be necessary to hold the new length (this requires that the vector be adjustable).

Note that vector-push-extend will increase the length of the vector by at least the amount of its extension argument, or a default amount defined by the implementation.  The programmer should take care when supplying this argument.  In SBCL v1.1.14, when no extension is supplied, the default is to double the length of the vector, subject to certain implementation constraints.  Because vector resizing is potentially an expensive operation, in order to avoid scaling issues when pushing large numbers of objects onto a vector it is generally recommended (not just in Lisp) that vectors be resized by a multiple of their length, not by a constant amount.  The programmer should keep this in mind if he or she is planning to supply an extension argument.

Keeping these warnings in mind, the C++ programmer can think of vector-pop and vector-push-extend as being much like the C++ STL std::vector operations pop_back() and push_back().

The less-familiar parts of Lisp for beginners — upgraded-complex-part-type

We’re now at one of those functions that I’ve never seen used.  The upgraded-complex-part-type function returns the most specialized type that can hold the passed type and be part of a complex number representation.  I don’t really have much to say about this.  It receives a subtype of real, and returns a subtype of real.  If somebody has actually used this in real code, please let me know in the comments.

The less-familiar parts of Lisp for beginners — upgraded-array-element-type

The next function we’re describing is upgraded-array-element-type.  This function takes a type specifier and returns another type specifier.  So, let’s talk about Lisp type specifiers a bit.  There are the expected ones for floating-point numbers, integers, complex numbers, and many more non-numeric types.  The programmer can also declare certain variables to be within a specified sub-range of the intrinsic numeric types.  For example, a variable can be declared as holding integers between 7 and 23, very much like the “subrange” types in the Pascal programming language.  The programmer might, however, be interested to know how that variable is stored in an array, where multiple objects of the same type might be laid out contiguously in memory.  The upgraded-array-element-type returns the most specific intrinsic type capable of holding the supplied type.  It allows the programmer to query the implementation and determine what intrinsic types are available.  I’ll post an example below.

This is a different perspective on data than that supplied in C++ by the stdint.h header file.  There, the programmer chooses types based on the minimum range that the variable is to hold and the desired tradeoff between speed and space.  The Lisp standard does not define something equivalent to, say uint_fast16_t, which might expand to different types on a 16-bit CPU and a 32-bit CPU, at the discretion of the implementation(s).

So, an example of the use of upgraded-array-element-type on SBCL 1.1.14:
*slime-repl sbcl*

CL-USER> (upgraded-array-element-type '(integer 7 23))
(UNSIGNED-BYTE 7)
CL-USER> (upgraded-array-element-type '(integer 0 1))
BIT
CL-USER> (upgraded-array-element-type '(integer -128 127))
(SIGNED-BYTE 8)
CL-USER> (upgraded-array-element-type '(integer 0 1000000))
(UNSIGNED-BYTE 31)
CL-USER> (upgraded-array-element-type '(integer 0 3000000000))
(UNSIGNED-BYTE 32)
CL-USER> (upgraded-array-element-type '(double-float 0.0d0 2.0d0))
DOUBLE-FLOAT