Series: common-lisp August 30, 2015
On the way to resume the development of
Ulquikit, I needed a way to retrieve all
functions from a package so that a Ulquikit command could be automatically
detected and invoked when necessary. Common Lisp doesn’t provide this out of
the box. However, it’s very easy to build one with do-all-symbols.
do-all-symbols takes the following form: (do-all-symbols (symbol package)
*body), binding symbol to all symbols in package one at a time and
execute body each time. A function symbol can be checked with fboundp,
which returns t if that symbol corresponds to a function. We then have the
first working version below:
(defun all-function-symbols (package)
"Retrieves all functions in a package."
(let ((res (list)))
(do-all-symbols (sym package)
(when (fboundp sym)
(push sym res)))
res))
Let’s try it out:
(defpackage #:foobar
(:use :cl)
(:export #:public-func))
(in-package #:foobar)
(defun private-func () (format t "A private function~%"))
(defun public-func () (format t "A public function~%"))
(in-package :cl-user)
(format t "~{~A~%~}" (all-function-symbols :foobar))
This simple implementation introduces two problems: 1) it returns imported
functions from other packages that the current package uses, and 2) it
doesn’t check if package is a package designator. Both are easily tackled.
The second problem could be solved by checking if the return value of
(find-package package) is not nil. The first problem is then consequently
solved by capturing the return value of find-package and check if it’s
eql-ed to the corresponding package of sym using (symbol-package sym).
In short, the condition looks like:
(when (and (fboundp sym)
(eql (symbol-package sym)
(find-package package)))
...)
Putting everything together:
(defun all-function-symbols (package)
"Retrieves all functions in a package."
(when (find-package package)
(let ((res (list)))
(do-all-symbols (sym package)
(when (and (fboundp sym)
(eql (symbol-package sym)
(find-package package)))
(push sym res)))
res)))
Afer refactoring, some optimization and error signaling, we have the final version:
(defun all-function-symbols (package-name)
"Retrieves all function symbols from a package."
(declare ((or package string symbol) package-name))
(the list
(let ((lst (list))
(package (find-package package-name)))
(cond (package
(do-all-symbols (symb package)
(when (and (fboundp symb)
(eql (symbol-package symb) package))
(push symb lst)))
lst)
(t
(error "~S does not designate a package" package-name))))))