anchor.diff:           :accessor name
            :type symbol
            :initform nil
  -         :documentation "")
  +         :documentation "
  +Name of the handler.
  +If defaults to the function name if `fn' is a named function.
  +This is useful so that the user can build handlers out of anonymous functions.")
      (description :initarg :description
                   :accessor description
                   :type string
                   :initform ""
  -                :documentation "")
  +                :documentation "
  +Description of the handler.  This is purely informative.")
      (fn :initarg :fn
          :accessor fn
          :type function
          :initform nil
  -       :documentation "")
  +       :documentation "
  +The handler function.  It can be an anonymous function.")
  +   (handler-type :initarg :handler-type
  +                 :accessor handler-type
  +                 :type list
  +                 :initform nil
  +                 :documentation "The function type of FN.
  +This is purely informative.")
      (place :initarg :place
             :accessor place
  -          :type list
  +          :type (or symbol list)
             :initform nil
  -          :documentation "")
  +          :documentation "
  +If the handler is meant to be a setter, PLACE describes what is set.
  +PLACE can be a symbol or a pair (CLASS SLOT).
  +This can be left empty if the handler is not a setter.")
      (value :initarg :value
             :accessor value
             :type t
             :initform nil
  -          :documentation "")))
  +          :documentation "
  +If the handler is meant to be a setter, VALUE can be used to describe what FN is
  +going to set to PLACE.
  +In particular, PLACE and VALUE can be used to compare handlers.
  +This can be left empty if the handler is not a setter.")))
   
   ;; TODO: Don't export make-handler, so that user is forced to use the typed versions.
  -;; But then how does the user define a custom-type handler?
  -(defun make-handler (fn &key name place value)
  +(defun make-handler (fn &key (class-name 'handler) name handler-type place value)
     "NAME is a symbol."
  -  (unless (typep fn 'function)          ; TODO: Should we allow symbols here?  It might be better to be stricter in terms of type-checking.
  -    (setf fn (function fn)))
     (setf name (or name (let ((fname (nth-value 2 (function-lambda-expression fn))))
                           (when (typep fname 'symbol)
                             fname))))
     (unless name
       (error "Can't make a handler without a name"))
  -  (make-instance 'handler
  +  (make-instance class-name
                    :name name
                    :fn fn
  +                 :handler-type handler-type
                    :place place
                    :value value))
  -
  -
  -(declaim (ftype (function ((function ()) &key (:name symbol) (:place t) (:value t))) make-void-handler))
  -(defun make-void-handler (fn &key name place value)
  -  "Make handler that accepts no argument."
  -  (make-handler fn :name name :place place :value value))
  -
  -(declaim (ftype (function ((function (&rest t)) &key (:name symbol) (:place t) (:value t))) make-*-handler))
  -(defun make-*-handler (fn &key name place value)
  -  "Make handler that accepts any argument."
  -  (make-handler fn :name name :place place :value value))
  -
  -(declaim (ftype (function ((function (number) number) &key (:name symbol) (:place t) (:value t)))
  -                make-number->number-handler))
  -(defun make-number->number-handler (fn &key name place value)
  -  (make-handler fn :name name :place place :value value))
  -
  -(declaim (ftype (function ((function (string) string) &key (:name symbol) (:place t) (:value t)))
  -                make-string->string-handler))
  -(defun make-string->string-handler (fn &key name place value)
  -  (make-handler fn :name name :place place :value value))
   
   (defmethod equals ((fn1 handler) (fn2 handler))
     "Return non-nil if FN1 and FN2 are equal."
  
positive.code:   (apply (apply #'alexandria:compose (mapcar #'fn (handlers hook))) args))
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Compile-time type checking for add-hook is useful in case `add-hook' is not
  ;; run when the user config is loaded.  We declaim the type for `add-hook'
  ;; because `satisfies' takes the object (the `add-hook' function) as argument,
  ;; but we have no way to access the hook or the handler passed as parameter.
  
  (defmethod add-hook ((hook hook) (handler handler) &key append)
    (serapeum:synchronized (hook)
      (if (not append)
          (pushnew handler (handlers hook) :test #'equals)
          (unless (member handler (handlers hook))
            (alexandria:appendf (symbol-value hook) (list handler))))))
  
  (defmethod add-hook ((hook hook) fn &key append)
    "Add FN to HOOK.
  FN cannot be an anonymous function.
  To add anonymous functions to HOOK, use
  
    (add-hook hook (make-handler (lambda () ...) :name ...))"
    (let ((handler (make-handler fn)))
      (add-hook hook handler :append append)))
  
  ;; (defmacro add-hook* (hook handler &key append)
  ;;   (when (hook-type hook)
  ;;     (assert (typep (fn handler) (hook-type hook)) ((fn handler))
  ;;             'type-error :datum (fn handler) :expected-type (hook-type hook)))
  ;;   `(progn
  ;;      (add-hook ,hook ,handler :append ,append)))
  
  (defmethod remove-hook ((hook hook) (fn handler))
    (serapeum:synchronized (hook)
  
negative.code: 
  ;; TODO: Because our hooks are typed, having both `run-hook` and
  ;; `run-hook-with-args' makes little sense.  Remove `run-hook-with-args'?
  ;; (defmethod run-hook-with-args ((hook hook) &rest args)
  ;;   (apply (combination hook) hook args))
  
  ;; TODO: Implement the following methods? Isn't the `combination' slot more general?
  ;; (defmethod run-hook-with-args-until-failure ((hook hook) &rest args)
  