Code: Select all
(defparameter *%function-types* (make-hash-table :test #'eq)) (defmacro defun* (name limited-lambda-list &body body) "Like defun, but saves type information. Special lambda lists (&optional, &key, etc.) not supported Lambda list treated like that of defmethod; ((integer a) b (integer c)) would yield variable a, b, and c where a&c are integers and b is of type T" (let ((ll (mapcar (lambda (itm) (if (consp itm) (cadr itm) itm)) limited-lambda-list)) (types (mapcar (lambda (itm) (if (consp itm) (car itm) t)) limited-lambda-list)) (docstring (if (stringp (car body)) (car body))) (body (if (stringp (car body)) (cdr body) body))) `(progn (if (fboundp ',name) (remhash (symbol-function ',name) *%function-types*)) (defun ,name ,ll ,docstring ,@(loop for var in ll for type in types collect `(check-type ,var ,type)) ,@body) (setf (gethash (symbol-function ',name) *%function-types*) ',types) ',name))) (defun check-function (function &rest types) "Checks that a given function (defined with defun*) is of the correct type" (let ((ftypes (gethash function *%function-types*))) (when (or (/= (length ftypes) (length types)) (notevery #'subtypep types ftypes)) (error "Function ~a is typed ~a, expected ~a" function ftypes types)))) (defun unregister-function (function) (remhash function *%function-types*))
2) check-function can be trivially improved to reject all untyped functions
3) The lambda list for the function defun* creates is helpful, though still limited.
4) It automatically checks to make sure the function was given the right types.
I'm thinking of making a simple prototype-based object system based on the above technique.