Or see the list of project sponsors.
This is a library written by François-René Rideau in days when he was the maintainer of the ASDF. This library allows you to move a piece of code, generated by a macro to the top-level of the lisp component.
This transformation happens during macro-expansion step.
As an example of this technique, "asdf-finalizers" contains a system "list-of" which defines a custom type "list-of":
POFTHEDAY> (asdf:load-system :list-of) T POFTHEDAY> (typep '(1 2 3 4 5) '(list-of:list-of integer)) T POFTHEDAY> (typep '(1 2 3 4 "Hello Lisp World") '(list-of:list-of integer)) NIL POFTHEDAY> (typep '((1 2 3)) '(list-of:list-of integer)) NIL POFTHEDAY> (typep '((1 2 3) (4 5 6)) '(list-of:list-of (list-of:list-of integer))) T POFTHEDAY> (typep '((1 2 "Not an integer") (4 5 6)) '(list-of:list-of (list-of:list-of integer))) NIL
It does this by creating a predicate function on the fly first time when you are using the type specifier. Type definition binds a function to the symbol and returns a type specifier (and list (satisfies list-of-integer-p)).
Of cause for different element types it uses their own predicate names.
To reproduce this type definition we could write such code:
POFTHEDAY> (deftype my-list-of (type) (let* ((name (format nil "LIST-OF-~S-P" type)) (predicate (intern name))) (format t "Creating predicate ~A for ~A~%" name type) (setf (symbol-function predicate) (lambda (x) (loop for c = x then (cdr c) while (consp c) always (typep (car c) type) finally (return (null c))))) `(and list (satisfies ,predicate)))) MY-LIST-OF POFTHEDAY> (typep '(1 2 3) '(my-list-of string)) Creating predicate LIST-OF-STRING-P for STRING NIL POFTHEDAY> (typep '("foo" "bar") '(my-list-of string)) T POFTHEDAY> (typep '(1 2 3) '(my-list-of integer)) Creating predicate LIST-OF-INTEGER-P for INTEGER T POFTHEDAY> (typep '(1 2 3) '(my-list-of integer)) T
But if we'll put this naive type definition into a library "my-list-of", then there will be problems.
During the first loading a system, which uses "my-list-of", everything will be ok. But when you'll try to load it into a fresh Lisp image, this UNDEFINED-FUNCTION error will be signalled:
The function MYLIST::LIST-OF-INTEGER-P is undefined.
This is because of SBCL processes "(my-list-of integer)" definitions during macro expansion step, before compilation. But does not do this when loading a compiled FASL.
In other words, using "(my-list-of integer)" in the code causes side-effects during macro expansion step. And when you are loading a precompiled code into a fresh Lisp image, these side-effects are not available anymore.
"asdf-finalizer" solves this problem, by placing additional code into the (eval-when (:compile-toplevel :load-toplevel :execute)) block. This code may recreate a side-effect during the :load-toplevel phase.
Here is the original "list-of" type definition:
(deftype list-of (type) (case type ((t) 'list) ;; a (list-of t) is the same as a regular list. ((nil) 'null) ;; a (list-of nil) can have no elements, it's null. (otherwise (let ((predicate (list-of-predicate-for type))) (eval-at-toplevel ;; now, and amongst final-forms if enabled `(ensure-list-of-predicate ',type ',predicate) `(fboundp ',predicate) ;; hush unnecessary eval-at-toplevel warnings "Defining ~S outside of finalized Lisp code" `(list-of ,type)) `(and list (satisfies ,predicate))))))
It uses a function "eval-at-toplevel", which helps "asdf-finalizer" to collect "(ensure-list-of-predicate 'integer 'list-of-integer-p)" forms into a special dynamic variables.
Later, these collected forms should be injected to the top-level with the call to "asdf-finalizers:final-forms":
(defpackage :test-list-of (:use :cl)) (in-package :test-list-of) (defun check-list-of () (typep '(1 2 3) '(list-of:list-of string))) (final-forms)
Also, you'll need to replace :file with :finalized-cl-source-file in the components section of your ASDF system definition. This will turn on "top-level forms" collection mechanism for this file.
This call to final-forms will be expanded into the forms, injected into a special variable during the macro expansion stage:
(eval-when (:compile-toplevel :load-toplevel :execute) (ensure-list-of-predicate 'integer 'list-of-integer-p) (ensure-list-of-predicate 'string 'list-of-string-p) ...)
Each call to "ensure-list-of-predicate" will recreate a predicate function during when the compiled code will be loaded into the fresh Lisp image.
You can also use this technic to inject any code from macroses into the top-level. Just call "asdf-finalizers:eval-at-toplevel" or "asdf-finalizers:register-final-form" from the macro's code and don't forget to insert "(final-forms)" to the end of files where these macroses will be used.
As a bonus for everybody who is interested to learn how does code processing work in Common Lisp, there is a great @ngnghm's article about Common Lisp code processing stages and eval-when usage:
Fare shared a piece of history explaining why "asdf-finalizers" were created: