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

From: akater <[email-masked]>
Date: Mon, 27 Sep 2021 01:09:00 +0000

I don't use CLOS or EIEIO very often but when I do, I remove methods all
the time.  I thus think it's a very important part of CLOS worflow.
SLIME inspector (for Common Lisp) offers buttons that remove methods on
the gf's page, e.g.:

Name: PRINT-OBJECT
Arguments: (SB-PCL::OBJECT STREAM)
Method class: #<STANDARD-CLASS COMMON-LISP:STANDARD-METHOD>
Method combination: #<SB-PCL::STANDARD-METHOD-COMBINATION STANDARD () {10002158A3}>
Methods: 
(TRACE-ENTRY T) [remove method]
(UNREADABLE-RESULT T) [remove method]
(CHANNEL T) [remove method]
(CONNECTION T) [remove method]
...

[remove method] are buttons.  Sadly, Elisp doesn't have an inspector but
I've heard of one effort github.com/mmontone/emacs-inspector and anyway,
these buttons would be appropriate in any displayed list of methods,
including the list currently shown in *Help* buffer for the gf.

Example:

Implementations:

[remove] :around (object stream) in ‘cl-print.el’.

Undocumented

[remove] ((object string) stream) in ‘cl-print.el’.

...

Recently it became necessary to use cl-remove-method in a library code
so I tried to write it.

The following seems to work but I'm very far from being confident about
it.  Also, methods are cached in lambdas returned by lambdas returned by
cl--generic-get-dispatcher, and I haven't yet figured out a way to get
them out of there; I'm also not sure if they should be removed manually,
or the tables will just get GC'ed given the proposed implementation of
cl-remove-method as is.

>From 50dc42ce1ea504657ccdcf85e9c71a2f27109610 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 | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 1640975b84..7d5c8ddc0d 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -98,7 +98,7 @@
 ;; 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))
 
 (cl-defstruct (cl--generic-generalizer
@@ -1255,6 +1255,35 @@ defun cl--generic-struct-specializers (tag &rest _)
 (cl--generic-prefill-dispatchers 0 integer)
 (cl--generic-prefill-dispatchers 0 cl--generic-generalizer integer)
 
+(cl-defmethod cl-remove-method ((generic-function cl--generic) method)
+  "An equivalent of Common Lisp's method for remove-method
+specialized on
+(COMMON-LISP:STANDARD-GENERIC-FUNCTION COMMON-LISP:METHOD)."
+  (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)
+  "For Common Lisp compatibility in Elisp.
+
+Namely, (cl-remove-method #'f ..) should work correctly but #'f returns symbol in Elisp."
+  (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