Paste number 92896: BetaCLOS

Paste number 92896: BetaCLOS
Pasted by: pcostanza
When:8 months, 13 hours ago
Share:Tweet this! | http://paste.lisp.org/+1ZOG
Channel:None
Paste contents:
Raw Source | XML | Display As
(in-package :closer-common-lisp-user)

#|
Here is an implementation of the an object-oriented extension suggested by
Goldberg, Findler and Flatt and discussed, for example, here:
http://lambda-the-ultimate.org/node/3731

This code relies on Closer to MOP which can be found at
http://common-lisp.net/project/closer/

The code has been successfully tested in Allegro Common Lisp, clisp,
Clozure Common Lisp, LispWorks and Steel Bank Common Lisp.

Thanks to Gareth McCaughan for improvements.
|#

(eval-when (:compile-toplevel :load-toplevel :execute)
  
  (defclass beta-generic-function (standard-generic-function) ()
    (:metaclass funcallable-standard-class))

  (defclass beta-method (standard-method)
    ((betap :reader betap
            :initarg :betap
            :initform nil))
    #+clozure (:default-initargs :closer-patch t))

  (defmethod initialize-instance :around
    ((method beta-method) &rest initargs &key qualifiers)
    (declare (dynamic-extent initargs))
    (if (equal qualifiers '(:beta))
      (apply #'call-next-method method
             :qualifiers ()
             :betap t
             initargs)
      (call-next-method)))

  (defun collect-runs (methods)
    (let ((complete-runs nil)
          (current-run nil))
      (flet ((complete-run ()
               (when current-run
                 (push (nreverse current-run) complete-runs)
                 (setf current-run nil))))
        (loop for method in methods do
              (when (betap method) (complete-run))
              (push method current-run))
        (complete-run))
      complete-runs))

  (define-method-combination beta ()
    ((around (:around))
     (before (:before))
     (primary () :required t)
     (after (:after)))
    (flet ((call-methods (methods)
             (mapcar (lambda (method) `(call-method ,method))
                     methods)))
      (let ((form (if (or before after (rest primary))
                    (let ((runs (collect-runs primary)))
                      `(multiple-value-prog1
                           (progn ,@(call-methods before)
                             (call-method ,(first (first runs))
                                          ,(rest (first runs))
                                          :inner-runs ,(rest runs)))
                         ,@(call-methods (reverse after))))
                    `(call-method ,(first primary)))))
        (if around
          `(call-method ,(first around)
                        (,@(rest around)
                         (make-method ,form)))
          form))))

  (defmethod make-method-lambda
             ((gf beta-generic-function) method-prototype
              lambda-expression environment)
    (declare (ignore method-prototype environment))
    (let ((method-args (gensym))
          (next-methods (gensym))
          (generic-function (gensym))
          (method (gensym))
          (inner-runs (gensym)))
      `(lambda (,method-args &optional ,next-methods
                             &key ((:generic-function ,generic-function))
                             ((:method ,method))
                             ((:inner-runs ,inner-runs)))
         (declare (ignorable ,next-methods ,generic-function ,method ,inner-runs))
         (flet ((call-next-method (&rest args)
                  (declare (dynamic-extent args))
                  (if (null ,next-methods)
                    (apply #'no-next-method ,generic-function ,method args)
                    (funcall (method-function (car ,next-methods))
                             (if args args ,method-args)
                             (cdr ,next-methods)
                             :generic-function ,generic-function
                             :method (car ,next-methods)
                             :inner-runs ,inner-runs)))
                (next-method-p () (not (null ,next-methods)))
                (call-inner-method (&rest args)
                  (declare (dynamic-extent args))
                  (if (null ,inner-runs)
                    (apply #'no-next-method ,generic-function ,method args)
                    (funcall (method-function (caar ,inner-runs))
                             (if args args ,method-args)
                             (cdar ,inner-runs)
                             :generic-function ,generic-function
                             :method (caar ,inner-runs)
                             :inner-runs (cdr ,inner-runs))))
                (inner-method-p () (not (null ,inner-runs))))
           (apply ,lambda-expression ,method-args)))))

  (defmacro define-beta-function (name (&rest args) &rest options)
    `(defgeneric ,name ,args
       ,@(unless (member :generic-function-class options :key #'car)
           '((:generic-function-class beta-generic-function)))
       ,@(unless (member :method-class options :key #'car)
           '((:method-class beta-method)))
       ,@(unless (member :method-combination options :key #'car)
           '((:method-combination beta)))
       ,@options)))


(defclass top () ())

(defclass middle (top) ())

(defclass bottom (middle) ())

(define-beta-function test (object))

(defmethod test ((object top))
  (print 'top))

(defmethod test :beta ((object middle))
  (print 'middle)
  (call-inner-method)
  (call-next-method)
  (values))
     
(defmethod test ((object bottom))
  (print 'bottom))

(defun run-test ()
  (test (make-instance 'bottom)))

This paste has no annotations.

Colorize as:
Show Line Numbers

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.