Subject: Re: Feature request: cl-remove-method (prototyped) and buttons for it

From: akater <[email-masked]>
Date: Mon, 27 Sep 2021 14:44:13 +0000

Various fixes to the prototype patch:
- require subr-x
- more specific default signature
- more extensive documentation

>From decd4e0ed24553981f9b87f59308a10ccdd1c5b6 Mon Sep 17 00:00:00 2001
From: akater <[email-masked]>
Date: Sun, 26 Sep 2021 21:33:46 +0000
Subject: [PATCH] Add cl-remove-method

---
 lisp/emacs-lisp/cl-generic.el | 56 ++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 1640975b84..c1ef3af3c7 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -98,8 +98,9 @@
 ;; usually be simplified, or even completely skipped.
 
 (eval-when-compile (require 'cl-lib))
-(eval-when-compile (require 'cl-macs))  ;For cl--find-class.
+(eval-when-compile (require 'cl-macs))  ;For cl--find-class, cl-loop
 (eval-when-compile (require 'pcase))
+(eval-when-compile (require 'subr-x))   ;if-let
 
 (cl-defstruct (cl--generic-generalizer
                (:constructor nil)
@@ -1255,6 +1256,59 @@ defun cl--generic-struct-specializers (tag &rest _)
 (cl--generic-prefill-dispatchers 0 integer)
 (cl--generic-prefill-dispatchers 0 cl--generic-generalizer integer)
 
+(cl-defgeneric cl-remove-method (generic-function method)
+  "Remove METHOD from GENERIC-FUNCTION by modifying the
+GENERIC-FUNCTION (if necessary).
+
+`cl-remove-method' does not signal an error if METHOD is not one
+of the methods on the GENERIC-FUNCTION.")
+
+(cl-defmethod cl-remove-method ((generic-function cl--generic)
+                                (method cl--generic-method))
+  "An equivalent of Common Lisp's default method for remove-method.
+
+Compatibility note:
+The Common Lisp HyperSpec page on remove-method says
+in Arguments and Values:
+  > method---a method
+and Exceptional Situations: none.
+So then, according to
+1.4.4.3 The ``Arguments and Values'' Section of a Dictionary Entry
+http://www.lispworks.com/reference/HyperSpec/Body/01_ddc.htm
+
+the consequences are undefined if the second value is not a method.
+
+In cl-generic, `cl-remove-method' is not applicable to
+non-methods by default."
+  ;; Credit for compatibility note goes to
+  ;; beach from #commonlisp at libera.chat
+  (setf (cl--generic-method-table generic-function)
+        ;; delq could cause bugs, let's see if it does
+        (delq method (cl--generic-method-table generic-function)))
+
+  (cl-loop for k being hash-key in cl--generic-combined-method-memoization
+           when (and (eq generic-function (car k))
+                     (memq method (cdr k)))
+           do (remhash k cl--generic-combined-method-memoization))
+
+  ;; It might make sense to move this
+  (defalias (cl--generic-name generic-function)
+    (cl--generic-make-function generic-function))
+  ;; to an :after method
+  ;; but it's not even clear to me whether
+  ;; having such :after method would be compatible with Common Lisp standard.
+  generic-function)
+
+(cl-defmethod cl-remove-method ((generic-function symbol)
+                                (method cl--generic-method))
+  "For Common Lisp compatibility in Elisp.
+
+Namely, (cl-remove-method #'f ..) should work correctly but #'f
+returns symbol in Elisp, hence this method."
+  (if-let ((gf (cl--generic generic-function)))
+      (cl-remove-method gf method)
+    (error "No generic function named %s" generic-function)))
+
 ;;; Dispatch on major mode.
 
 ;; Two parts:
-- 
2.32.0