I sometimes use defmethod
to enforce types in functions since the syntax is nice.
Here is an utterly terrible plus function, for the sake of example.
(defgeneric plus (x y)
:documentation "a terrible + function")
(defmethod plus ((x integer) (y integer))
(+ x y))
Is there a way to seal my generic method so that other implementations can't be added to it?
You need to define a new type of generic functions, using the meta-object protocol:
(ql:quickload :closer-mop)
Here is the API of the small library:
(defpackage :sealable-gf
(:use :cl)
(:export
#:sealable-gf ;; class of generic functions that can be sealed
#:seal ;; sealing function
#:sealed-function ;; serious-error signaled on modification
#:function-of ;; accessor for the function stored in above error
))
And the implementation:
(in-package :sealable-gf)
Define the class of function, be sure to use the appropriate metaclass:
(defclass sealable-gf (c2mop:standard-generic-function)
((sealed :initform nil))
(:metaclass c2mop:funcallable-standard-class))
The function that seals a generic function:
(defun seal (g)
(check-type g sealable-gf)
(with-slots (sealed) g
(setf sealed t)))
The error:
(define-condition sealed-function (serious-condition)
((function :initarg :function :reader function-of)))
Plug our code before the add-method
function for our class:
(defmethod c2mop:add-method :before ((g sealable-gf) m)
(with-slots (sealed) g
(when sealed
(error 'sealed-function :function g))))
For example:
(defpackage :seal-user (:use :cl :sealable-gf))
(in-package :seal-user)
(defgeneric plus (a b)
(:documentation "add stuff generically")
(:method ((a integer) (b integer)) (+ a b))
(:generic-function-class sealable-gf))
(plus 3 2)
=> 5
(seal #'plus)
=> T
(defmethod plus ((a string) (b string))
(concatenate 'string a b))
;; Condition SEALABLE-GF:SEALED-FUNCTION was signalled.
;; [Condition of type SEALED-FUNCTION]
You can use check-type
or declare
to manipulate types, there is no need to use generic functions. Also, sealing goes against the open nature of generic functions, at least in my opinion CLOS is about leaving some space in your design for subclasses and new methods (the fact that we can answer positively here is because no one sealed add-method
, for example).