[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 15bbcf2: * packages/cl-generic: Add new package

From: Stefan Monnier
Subject: [elpa] master 15bbcf2: * packages/cl-generic: Add new package
Date: Tue, 03 Feb 2015 05:47:38 +0000

branch: master
commit 15bbcf24c4646d37b6e6fcf714b62f5151d85700
Author: Stefan Monnier <address@hidden>
Commit: Stefan Monnier <address@hidden>

    * packages/cl-generic: Add new package
 packages/cl-generic/cl-generic.el |  115 +++++++++++++++++++++++++++++++++++++
 1 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/packages/cl-generic/cl-generic.el 
new file mode 100644
index 0000000..060cffc
--- /dev/null
+++ b/packages/cl-generic/cl-generic.el
@@ -0,0 +1,115 @@
+;;; cl-generic.el --- Forward cl-generic compatibility for Emacs<25
+;; Copyright (C) 2015  Free Software Foundation, Inc
+;; Author: Stefan Monnier <address@hidden>
+;; vcomment: Emacs-25's version is 1.0 so this has to stay below.
+;; Version: 0.1
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; GNU General Public License for more details.
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+;;; Commentary:
+;; This is a forward compatibility package, which provides (a subset of) the
+;; features of the cl-generic package introduced in Emacs-25, for use on
+;; previous emacsen.
+;; Make sure this is installed *late* in your `load-path`, i.e. after Emacs's
+;; built-in .../lisp/emacs-lisp directory, so that if/when you upgrade to
+;; Emacs≥25, the built-in version of the file will take precedence, otherwise
+;; you could get into trouble (although we try to hack our way around the
+;; problem in case it happens).
+;; AFAIK, the main incompatibilities between cl-generic and EIEIO's defmethod
+;;  are:
+;; - EIEIO does not support multiple dispatch.  We ignore this difference here
+;;   and rely on EIEIO to detect and signal the problem.
+;; - EIEIO only supports primary, :before, and :after qualifiers.  We ignore
+;;   this difference here and rely on EIEIO to detect and signal the problem.
+;; - EIEIO does not support specializers other than classes.  We ignore this
+;;   difference here and rely on EIEIO to detect and signal the problem.
+;; - EIEIO uses :static instead of (subclass <foo>) and :static methods match
+;;   both class arguments as well as object argument of that class.  Here we
+;;   turn (subclass <foo>) into a :static qualifier and ignore the semantic
+;;   difference, hoping noone will notice.
+;; - EIEIO's defgeneric does not reset the function.  We ignore this difference
+;;   and hope for the best.
+;; - EIEIO uses `call-next-method' and `next-method-p' while cl-defmethod uses 
+;;   `cl-next-method-p' and `cl-call-next-method' (simple matter of renaming).
+;;   We handle that by renaming the calls in the `cl-defmethod' macro.
+;; - The errors signaled are slightly different.  We make
+;;   cl-no-applicable-method into a "parent" error of no-method-definition,
+;;   which should cover the usual cases.
+;; - EIEIO's no-next-method and no-applicable-method have different calling
+;;   conventions from cl-generic's.  We don't try to handle this, so just
+;;   refrain from trying to call (or add methods to) `cl-no-next-method' or
+;;   `cl-no-applicable-method'.
+;; - EIEIO's `call-next-method' and `next-method-p' have dynamic scope whereas
+;;   cl-generic's `cl-next-method-p' and `cl-call-next-method' are lexically
+;;   scoped.  The cl-defmethod here handles the common subset between the two.
+;;; Code:
+;; We need to handle the situation where this package is used with an Emacs
+;; that comes with a real cl-generic (i.e. ≥25.1).
+;; First line of defense: try to make sure the built-in cl-lib comes earlier in
+;; load-path so we never get loaded:
+;;;###autoload (let ((d (file-name-directory #$)))
+;;;###autoload   (when (member d load-path)
+;;;###autoload     (setq load-path (append (remove d load-path) (list d)))))
+(require 'cl-lib nil 'noerror)
+;; In Emacs≥25, cl-lib autoloads cl-defmethod and friends.
+(unless (fboundp 'cl-defmethod)
+  (require 'eieio)
+  (require 'cl)                         ;For `labels'.
+  (defalias 'cl-defgeneric 'defgeneric)
+  ;; Compatibility with code which tries to catch
+  ;; `cl-no-applicable-method' errors.
+  (push 'cl-no-applicable-method (get 'no-method-definition 'error-conditions))
+  (defmacro cl-defmethod (name args &rest body)
+    (let ((qualifiers nil))
+      (while (not (listp args))
+        (push args qualifiers)
+        (setq args (pop body)))
+      (when (eq (car-safe (car args)) 'subclass)
+        ;; There's no exact equivalent to `subclass', but :static
+        ;; provides a superset which should work just as well in practice.
+        (push :static qualifiers)
+        (setcar args (cadr (car args))))
+      (let ((docstring (if (and (stringp (car body)) (cdr body)) (pop body))))
+        `(defmethod ,name ,@qualifiers ,args
+           ,docstring
+           ;; We could just alias `cl-call-next-method' to `call-next-method',
+           ;; and that would work, but then files compiled with this cl-generic
+           ;; wouldn't work in Emacs-25 any more.
+           ;; Also we use `labels' rather than one of cl-lib's macros, so as to
+           ;; be compatible with older emacsen (and ELPA's cl-lib emulation
+           ;; doesn't provide cl-flet and provides an incomplete cl-labels).
+           ,@(if qualifiers
+                 ;; Must be :before or :after, so can't call next-method.
+                 body
+               `((labels ((cl-call-next-method (&rest args)
+                                               (apply #'call-next-method args))
+                          (cl-next-method-p () (next-method-p)))
+                   ,@body))))))))
+(provide 'cl-generic)
+;;; cl-generic.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]