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

Parallel/Similar Flavors



Let me summarize your problem as I understand it, to make sure we're
discussing the same problem.

    You want to have multiple flavors which implement essentially the same
    algorithm for the same operation, except that in one of each pair you want
    to test some instance variable before proceeding, while in the other you
    want to proceed because the "value" of that "instance variable" will
    always be non-NIL.

    Additionally, you want to expand this code inline (using macros) because
    you don't want to pay the additional expense of the function call to a
    subroutine method.

Here's the rub: you need to compile two different functions if you want to
have two different behaviours in the compiled code.

So, what you would need to do would be something like this:

(defmethod (#1=frobize full-blown-switchable-frob) #2=(...)
  #3=(code-involving-WHEN-SWITCH-macro))

(defmethod (#1# full-blown-nonswitchable-frob) #2#
  #3#)

What you really want is a DEFMETHOD-like macro which defines both of the
above:

(defmacro define-frob-method ((name &rest defmethod-options) lambda-list &body body)
  `(progn (defmethod (,name full-blown-switchable-frob ,@defmethod-options)
		     ,lambda-list
	    (macrolet ((when-switch (&body body)
			 `(when switch ,@body)))
	      ,@body))
	  (defmethod (,name full-blown-nonswitchable-frob ,@defmethod-options)
		     ,lambda-list
	    (macrolet ((when-switch (&body body)
			 `(progn ,@body)))
	      ,@body))))

You would probably want a DEFWHOPPER variant, and maybe a DEFUN-IN-FLAVOR
variant as well.  Of course, you could continue to use DEFMACRO-IN-FLAVOR for
the definition of WHEN-SWITCH instead of the MACROLET I have used.  You might
want to construct the name of the flavor from an argument to DEFINE-FROB-METHOD
instead of making it be constant; this is relatively easy using SYS:FINTERN for
construction of the name and SI:READ-STRING-LIST-FROM-STRING for decomposition
of the old name (example below).

Please note well that two functions (e.g., methods) are REQUIRED by your
desiderata; there is no getting around this.

;;; Here is a version which generates the nonswitchable name from the
;;; switchable.  It should be obvious how to make this do what you want in
;;; general.  This version does not use MACROLET.
(defmacro define-frob-method
	  ((name switchable-flavor &rest defmethod-options)
	   lambda-list &body body)
  (let* ((flavor-name-parts (si:read-string-list-from-string switchable-flavor 0 nil #\-))
	 (nonswitchable-flavor
	   (sys:fintern "~{~A~^-~}"
			(substitute "NONSWITCHABLE" "SWITCHABLE" flavor-name-parts 
				    :test #'string-equal))))
    `(progn (defmethod (,name ,switchable-flavor ,@defmethod-options)
		       ,lambda-list
	      ,@body)
	    (defmethod (,name ,nonswitchable-flavor ,@defmethod-options)
		       ,lambda-list
	      ,@body))))

;;;(define-frob-method (frobize basic-switchable-frob :after) (foo)
;;;  (when-switch (setf bar foo)))
;;; Generates the following code:
;;;(PROGN (DEFMETHOD (FROBIZE BASIC-SWITCHABLE-FROB :AFTER) (FOO)
;;;         (WHEN-SWITCH (SETF BAR FOO)))
;;;       (DEFMETHOD (FROBIZE BASIC-NONSWITCHABLE-FROB :AFTER) (FOO)
;;;         (WHEN-SWITCH (SETF BAR FOO))))