Methods with incongruent lambda lists.
Peter von Etter
* Incongruent methods ** Basics Incongruent methods is a simple implementation of incongruent (overloaded) methods using standard CLOS methods. These methods do not support &key, &rest, &optional etc. Example: (define-incongruent-method hello ((me string)) (concatenate 'string "Hello, " me)) (define-incongruent-method hello ((me string) (greeting string)) (concatenate 'string greeting ", " me)) (hello "world") ==> "Hello world" (hello "world" "Hey") ==> "Hey world" Example 2: (defclass hello () ((message :initarg :message :initform nil :accessor %get-message))) ;; standard generic accessor. (define-incongruent-method greet ((me hello)) (print (%get-message me))) (define-incongruent-method greet ((me hello) (n integer)) (dotimes (m n) (greet me))) ** Shared methods Methods that are "shared" between packages can be also be defined. These methods can be called using the "imcall" function. It is advisable that shared methods are specialized on at least one class defined in your own system. A shared method specializing only on, say, the built-in integer class, might accidentally get redefined by other systems. - Operations that are part of the "public interface" of a class are good candidates for shared methods. - In the following example, notice how the call to "notify" in the do-something method refers to test-package-1::notify, not test-package-2::notify. In fact, test-package-1 has no knowledge of the other package and can be compiled separately if one wishes. Example: (defpackage :test-package-1 (:use :cl :incongruent-methods) (:export #:my-class)) (in-package :test-package-1) (defclass my-class () ((slot :accessor %get-slot))) (define-shared-method slot-contents ((me my-class)) (slot-value me 'slot)) (define-shared-method get-something ((me my-class)) 'something) (define-shared-method do-something ((me my-class) (object t)) (format t "I did something!~%") (imcall 'notify object :done)) ;;;; (defpackage :test-package-2 (:use :cl :incongruent-methods)) (in-package :test-package-2) (defclass some-random-class () ()) (define-shared-method notify ((me some-random-class) (status symbol)) (format t "Received status: ~A~%" status)) (defun main () (let ((my-object (make-instance 'test-package-1:my-class)) (some-object (make-instance 'some-random-class))) (format t "~S~%" (imcall 'get-something my-object)) (imcall 'do-something my-object some-object))) CL-USER> (main) ==> TEST-PACKAGE-1::SOMETHING I did something! Received status: DONE ** Define-class A convenience macro for defining classes is also provided. The symbol "ME" in the body of the methods means the current object (self, this etc). It should also be the first argument when calling methods defined using this macro. Accessors will be converted to incongruent methods. Example: (define-class greeting () ((message :accessor message :initform nil) (some-slot :accessor some-slot :initform nil)) ;; Methods can be defined here, if one wants. (greet () ;; lambda list is actually ((me greeting)) (message me)) (greet ((n integer)) ;; lambda list is actually ((me greeting) (n integer)) (dotimes (m n) (greet me))) (greet ((n (eql 42))) (some-slot me)) ((setf greet) (new) (setf (message me) new))) (let ((i (make-instance 'greeting :message "Hello"))) (greet i)) ==> "Hello" (let ((i (make-instance 'greeting :message "Hello"))) (setf (greet i) "Hi") (greet i)) ==> "Hi"