- 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)))
(if (fboundp ',name)
(remhash (symbol-function ',name) *%function-types*))
(defun ,name ,ll
,@(loop for var in ll for type in types collect `(check-type ,var ,type))
(setf (gethash (symbol-function ',name) *%function-types*)
(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*))
1) check-function is safe to use on untyped functions.
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.