Re: most specific class instantiation
Hi,
This is an instance of the "make isn't generic" problem - see http://
www2.parc.com/csl/groups/sda/publications/papers/Kiczales-ISOTAS93/
(With "make" they mean something like "make-instance" here.)
Your approach is probably good enough for your concrete problem at
hand. It appears to be inelegant because this is actually a case of a
hidden dependency on control flow. This becomes clearer when the code
is broken down into smaller parts:
(defmethod import ((a virtual-a) l)
(dolist (item l)
(insert a (create-b item))))
(defun create-b (item) ...)
In this version, create-b cannot decide anymore which kind of b to
instantiate. To be able to do this, a has to be passed as another
parameter:
(defmethod import ((a virtual-a) l)
(dolist (item l)
(insert a (create-b a item))))
(defun create-b (a item)
(make-corresponding-b a :item item))
Whenever a parameter has to be passed around down a call chain,
without changing it, we are actually simulating dynamic scoping:
(defvar *a*) ; now a special variable
(defmethod import ((*a* virtual-a) l)
(dolist (item l)
(insert *a* (create-b item))))
(defun create-b (item)
(make-corresponding-b *a* :item item))
This is the reason why the paper above essentially turns make-
instance / make into something similar to a dynamically scoped
function, because this allows changing its behavior based on the
control flow.
In a Java setting, you could use AspectJ and define a cflow-style
pointcut to modify invocations of "new". In Common Lisp, you could
use ContextL and put all behavior related to virtual types in one
layer and all behavior related to concrete types in another, and then
use dynamically scoped activation/deactivation of layers to control
instance creation. (You would have to use a replacement for make-
instance that is defined as a layered function to make this work.)
Note that the code above (with the special variable *a*) is only for
illustration - I am not arguing that this is good programming
style. ;) AspectJ or ContextL would work, but are probably overkill
for your specific case.
Pascal
On 25 Jul 2006, at 19:29, Taylor, Joshua wrote:
> Hello all,
>
> Be forewarned, this is not a Lispworks specific question. There are a
> number of very
> skilled people who read this list however, and I hope that someone
> might be able to
> help me with what I'm about to ask.
>
> In using methods and classes which inherit from others, it's seems
> easy to come up with
> a hierarchy similar to the following.
>
> Class virtual-A is a container which can contain items of type
> Virtual-B. There are various actions that can happen to contents of a
> virtual-A, and some of them might affect other contents
> in the instance. For instance, inserting a virtual-b into a virtual-a
> means that every item in the virtual-a needs to be frobbed.
> That might wrriten
>
> (defmethod insert ((a virtual-a) (b virtual-b))
> ...
> (dolist (item (contents a))
> (frob item))
> ...)
>
> Now perhaps I make a concrete-a and concrete-b, which inherit from
> their virtual
> counterparts. When a concrete-b is inserted into a concrete-a, the
> frobbing still
> needs to happen, but there is no additional action necessary. The
> same insert
> method used above will work.
>
> Now, suppose I have an import procedure which takes an A (either
> virtual or concrete)
> and a list of items from which B's (virtual or concrete) can be
> construcred and constructs
> the objects and inserts them. It seems wasteful to have two, almost
> identical methods:
>
> (defmethod import ((a virtual-a) l)
> (dolist (item l)
> (insert a (make-instance 'virtual-b :item item))))
>
> (defmethod import ((a concrete-a) l)
> (dolist (item l)
> (insert a (make-instance 'virtual-b :item item))))
>
> One approach would be to not use make-instance, but rather some
> sort of
> make-corresponding-b, so as to write:
>
> (defmethod import ((a virtual-a) l)
> (dolist (item l)
> (insert a (make-corresponding-b a :item item))))
>
> where the idea is that make-corresponding-b would be overridden to
> make whatever type of
> B was suited to whatever type of A was provided. That seems somewhat
> hackish to me
> though.
>
> The issue seems to be that methods are usually written for the most
> general case, so as to
> keep special cases in the special methods where they belong. However,
> what I'm looking for
> here is some way to get the most specific type possible, but in a
> general way.
>
> I'm going to continue researching into this sort of thing, but if
> anyone knows of any approach
> to this problem, or an alternative, or could just tell me why this is
> a *wrong* way to do something,
> I'm all ears and would be greatly appreciative!
>
> Thanks!
>
> --
> =====================
> Joshua Taylor
> tayloj@rpi.edu
>
--
Pascal Costanza, mailto:pc@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium