 
 ;;; Commentary:
 
-;; FIXME: `el-patch-validate' needs to be smarter, so el-patch can be
-;; properly idempotent in all situations.
+;; Yet to come.
 
 ;;; Code:
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; Libraries
+
 (require 'cl-lib)
-(require 'subr-x)
-
-(defvar el-patch-validate t
-  "If this variable is non-nil, then el-patch will validate your
-patches and issue a warning if a patched function has changed
-since it was originally patched. Otherwise, validation is skipped
-and patches act as simple overrides.
-
-It is recommended that you set this variable to nil in your
-init-file before loading el-patch-validate. This will improve
-performance and suppress unnecessary messages. However, whenever
-you upgrade Emacs or your packages, you should set this variable
-to a non-nil value and reload your init-file, to see if any of
-your patches might need to be updated.")
-
-(defvar el-patches (make-hash-table :test 'equal)
-  "Hash table of patches attempted. Primarily for internal use;
-end users will likely prefer the interactive functions
-`el-patch-inspect' and `el-patch-forget'.
-
-The hash table maps symbols (function names) to lists of patches.
-Each patch is represented as a list (patch definition, actual
-function definition, expected old function definition, new
-function definition).")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; Internal variables
+
+(defvar el-patch--patches (make-hash-table :test 'equal)
+  "Hash table of patches that have been defined. The keys are
+symbols that are function names. The values are patch
+definitions, which are lists beginning with `defun', `defmacro',
+etc.")
+
+(defvar el-patch--not-present 'key-is-not-present-in-hash-table
+  "Value used as a default argument to `gethash'.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; Resolving patches
 
 (defmacro el-patch--with-puthash (table kvs &rest body)
-  "Binds each of the KVS (lists whose car is the key and whose
-cadr is the value) in the hash TABLE, evaluates BODY, then
-restores the original state of TABLE. Return value is the result
-of evaluating the last form in BODY."
+  "Binds each of the KVS (lists whose first element is the key
+and whose second element is the value) in the hash TABLE,
+evaluates BODY, then restores the original state of TABLE. Return
+value is the result of evaluating the last form in BODY."
   (declare (indent 2))
   `(let* ((table ,table)
           (kvs ,kvs)
           (original-kvs (mapcar (lambda (kv)
-                                  (list (car kv) (gethash (cadr kv) table)))
+                                  (list (car kv)
+                                        (gethash (cadr kv) table
+                                                 el-patch--not-present)))
                                 kvs)))
      (prog2
          (dolist (kv kvs)
            (puthash (car kv) (cadr kv) table))
          (progn ,@body)
        (dolist (kv original-kvs)
-         (puthash (car kv) (cadr kv) table)))))
-
-(defun el-patch-resolve (form new &optional table)
+         ;; Note that we can't distinguish between a missing value and
+         ;; a value that is coincidentally equal to
+         ;; `el-patch--not-present', due to limitations in the Emacs
+         ;; Lisp hash table API.
+         (if (equal (car kv) el-patch--not-present)
+             (remhash (car kv) table)
+           (puthash (car kv) (cadr kv) table))))))
+
+(defun el-patch--resolve (form new &optional table)
   "Resolves a patch FORM, returning a list of forms to be spliced
 into the surrounding s-expression. Resolves in favor of the
 original version if NEW is nil; otherwise resolves in favor of

  (declare (indent 0))
  (error "Can't use `el-patch-swap' outside of an `el-patch'"))

(defmacro el-patch-wrap (&optional triml &optional trimr &rest args)
  "Patch directive. In the original definition, the ARGS are
spliced into the containing s-expression. If TRIML is an integer,
the first TRIML of the ARGS are removed first. If TRIMR is an

                                 directive))
               (inverted (not (equal this-directive directive)))
               (this-new (if inverted (not new) new))
               (resolve (lambda (form) (el-patch--resolve form new table))))
          (pcase this-directive
            ((quote el-patch-add)
             (when (<= (length form) 1)
