+++ /dev/null
-(in-package "SPECIALIZABLE")
-
-(defclass prototype-object ()
- (;; FIXME: we should add slots at some point
- (delegations :initarg :delegations :accessor delegations)
- (roles :initform (make-array 0 :adjustable t :fill-pointer t)
- :accessor roles)
- ;; debugging aid
- (name)))
-(defmethod print-object ((o prototype-object) s)
- (if (slot-boundp o 'name)
- (format s "~S" (slot-value o 'name))
- (print-unreadable-object (o s :type t :identity t)
- (format s "[~{~S~^, ~}]" (delegations o)))))
-(defun add-delegation (obj del)
- (push del (delegations obj)))
-(defun remove-delegation (obj)
- (pop (delegations obj)))
-(defun map-delegations (fun obj)
- (funcall fun obj)
- ;; FIXME: should we maintain a table of visited nodes? Should it be
- ;; topologically sorted? Section 5.3 in PwMD [Salzman & Aldrich]
- ;; suggests not, at least for now
- (mapc (lambda (o) (map-delegations fun o)) (delegations obj))
- nil)
-(defstruct (role (:type list) (:constructor make-role (method argpos)))
- method argpos)
-(defun add-role (obj role)
- (let ((pos (role-argpos role))
- (roles (roles obj)))
- (unless (< pos (length roles))
- (dotimes (i (- (1+ pos) (length roles)))
- (vector-push-extend nil roles)))
- (pushnew (role-method role) (aref roles pos))))
-(defun remove-role (obj role)
- (let ((pos (role-argpos role)))
- (setf (aref (roles obj) pos)
- (remove (role-method role) (aref (roles obj) pos)))
- (tagbody
- start
- (when (or (= (length (roles obj)) 0)
- (aref (roles obj) (1- (length (roles obj)))))
- (go done))
- (vector-pop (roles obj))
- (go start)
- done)))
-(defun map-roles (fun obj)
- (dotimes (i (length (roles obj)))
- (dolist (m (aref (roles obj) i))
- (funcall fun (make-role m i)))))
-(defun find-role (role obj)
- (when (< (role-argpos role) (length (roles obj)))
- (find (role-method role) (aref (roles obj) (role-argpos role)))))
-(defmacro do-roles ((rvar form &optional result) &body body)
- `(progn (map-roles (lambda (,rvar) ,@body) ,form) ,result))
-(defun clone (p)
- (let ((result (make-instance 'prototype-object
- :delegations (copy-list (delegations p)))))
- (do-roles (r p result)
- (add-role result r))))
-
-;;; redefinition semantics are interesting. We need the INFO here so
-;;; that we can implement specializer-accepts-p, which must be able to
-;;; lookup the particular method/argpos that the specializer
-;;; represents. But we must also be able to redefine methods in a way
-;;; that isn't insane, which I think implies that same-specializer-p
-;;; should ignore the INFO and just use the OBJECT.
-(defclass prototype-specializer (extended-specializer)
- ((role :accessor prototype-specializer-role)
- (object :initarg :object :accessor prototype-specializer-object)))
-(defmethod print-object ((o prototype-specializer) s)
- (print-unreadable-object (o s :type t :identity t)
- (format s "~S" (prototype-specializer-object o))))
-(defmethod sb-pcl::same-specializer-p
- ((s1 prototype-specializer) (s2 prototype-specializer))
- (eql (prototype-specializer-object s1)
- (prototype-specializer-object s2)))
-(defclass prototype-generic-function (specializable-generic-function)
- ()
- (:metaclass sb-mop:funcallable-standard-class))
-(defmethod sb-pcl:make-method-specializers-form
- ((gf prototype-generic-function) method snames env)
- (flet ((frob (x)
- (typecase x
- (sb-mop:specializer x)
- (symbol `(make-instance 'prototype-specializer :object ,x))
- ((cons (eql 'class)) `(find-class ',(cadr x)))
- ((cons (eql 'eql)) `(sb-mop:intern-eql-specializer ,(cadr x)))
- (t (error "unexpected specializer name: ~S" x)))))
- `(list ,@(mapcar #'frob snames))))
-(defmethod sb-pcl:parse-specializer-using-class
- ((gf prototype-generic-function) name)
- (make-instance 'prototype-specializer :object name))
-(defmethod sb-pcl:unparse-specializer-using-class
- ((gf prototype-generic-function) (s prototype-specializer))
- (let ((object (prototype-specializer-object s)))
- (if (slot-boundp object 'name)
- (slot-value object 'name)
- s)))
-
-(defmethod add-method :after ((gf prototype-generic-function) m)
- (let ((ss (sb-mop:method-specializers m)))
- (do* ((i 0 (1+ i))
- (ss ss (cdr ss))
- (s (car ss) (car ss)))
- ((null ss))
- (when (typep s 'prototype-specializer)
- (let ((object (prototype-specializer-object s))
- (role (make-role m i)))
- (setf (prototype-specializer-role s) role)
- (add-role object role))))))
-(defmethod remove-method :after ((gf prototype-generic-function) m)
- (let ((ss (sb-mop:method-specializers m)))
- (do* ((i 0 (1+ i))
- (ss ss (cdr ss))
- (s (car ss) (car ss)))
- ((null ss))
- (when (typep s 'prototype-specializer)
- (let ((object (prototype-specializer-object s))
- (role (make-role m i)))
- (setf (prototype-specializer-role s) nil)
- ;; this is one of the places where the semantics
- ;; are... dodgy. Removing the method from the generic
- ;; function, and the role from the object, doesn't affect
- ;; the roles in any clones. We could potentially use the
- ;; fact that once removed the method is no longer associated
- ;; with a generic function? Hm, C-A-M will not consider the
- ;; removed method for applicability...
- (remove-role object role))))))
-
-(defmethod generalizer-of-using-class
- ((gf prototype-generic-function) (object prototype-object))
- object)
-
-(defmethod specializer-accepts-generalizer-p
- ((gf prototype-generic-function) (s prototype-specializer) object)
- (values (specializer-accepts-p s object) t))
-
-(defmethod specializer-accepts-p ((specializer prototype-specializer) object)
- (cond
- ((not (typep object 'prototype-object)) nil)
- ((eql (prototype-specializer-object specializer) /root/) t)
- (t
- (let ((role (prototype-specializer-role specializer)))
- (map-delegations
- (lambda (o)
- (when (find-role role o)
- (return-from specializer-accepts-p t)))
- object)))))
-
-(defmethod specializer< ((gf prototype-generic-function) (s1 prototype-specializer) (s2 prototype-specializer) g)
- (let ((o1 (prototype-specializer-object s1))
- (o2 (prototype-specializer-object s2)))
- (map-delegations
- (lambda (o)
- (cond
- ((eql o o1) (return-from specializer< '<))
- ((eql o o2) (return-from specializer< '>))))
- g)
- '=))
-
-(defmethod compute-applicable-methods-using-generalizers ((gf prototype-generic-function) generalizers)
- (values nil nil))
-(defmethod generalizer-equal-hash-key ((gf prototype-generic-function) (g prototype-object))
- g)
-
-(defmacro defpvar (name value)
- `(let ((val ,value))
- (setf (slot-value val 'name) ',name)
- (defparameter ,name val)))
-
-(defpvar /root/ (make-instance 'prototype-object :delegations nil))
-(defpvar /animal/ (clone /root/))
-(defpvar /fish/ (clone /root/))
-(defpvar /shark/ (clone /root/))
-(defpvar /healthy-shark/ (clone /root/))
-(defpvar /dying-shark/ (clone /root/))
-(add-delegation /fish/ /animal/)
-(add-delegation /shark/ /animal/)
-(add-delegation /shark/ /healthy-shark/)
-(defgeneric encounter (x y)
- (:generic-function-class prototype-generic-function))
-(defmethod encounter ((x /fish/) (y /healthy-shark/))
- (format t "~&~A swims away~%" x))
-(defmethod encounter ((x /fish/) (y /animal/))
- x)
-(defgeneric fight (x y)
- (:generic-function-class prototype-generic-function))
-(defmethod fight ((x /healthy-shark/) (y /shark/))
- (remove-delegation x)
- (add-delegation x /dying-shark/)
- x)
-(defmethod encounter ((x /healthy-shark/) (y /fish/))
- (format t "~&~A swallows ~A~%" x y))
-(defmethod encounter ((x /healthy-shark/) (y /shark/))
- (format t "~&~A fights ~A~%" x y)
- (fight x y))