We recently covered the with-accessors macro. That allowed the programmer to create a lexical environment in which a symbol substituted for a call to an accessor in a particular instance. The with-slots macro is essentially the same, but rather than using one of the class’ accessors, it expands into a slot-value call. We’ve discussed when slot-value is recommended, those considerations also apply, naturally, to with-slots.
The with-package-iterator macro gives access to the symbols in one or more packages, optionally filtering by whether the symbols are external, internal, or inherited. The macro allows the programmer to retrieve essentially the same information available with do-symbols, but with a different API.
Frankly, the presence of this macro is a bit surprising to me. I don’t see how it adds any behaviour that can’t be easily reproduced with a simple application of do-symbols, and the Lisp standard does demonstrate fairly good orthogonality, so I don’t really understand what with-package-iterator is doing as part of the standard. If anybody can enlighten me in the comments, that would be nice.
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 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 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().