Path: utzoo!attcan!uunet!lll-winken!ames!ucsd!ucsbcsl!eiffel!bertrand From: bertrand@eiffel.UUCP (Bertrand Meyer) Newsgroups: comp.lang.eiffel Subject: Re: Posting on behalf of CNET users [Answer, part 1] Summary: For more than one action, use repeated inheritance Message-ID: <123@eiffel.UUCP> Date: 19 Mar 89 20:09:21 GMT References: <112@eiffel.UUCP> <114@eiffel.UUCP> Organization: Interactive Software Engineering, Santa Barbara CA Lines: 292 From , jwg1@bunny.gte.com (James W. Gish): > Bertrand, I think you missed one part of the problem: what if I want > to have more than one action that may be applied to one type. For example, > "action" may be either "double" or "halve" or "triple" on sets of integers. > > I want to be able to do this without repeating the basic form of the > iteration through the set. Mr. Gish is right. I missed part of the problem. This message introduces the generalization of the technique described in my previous posting <114@eiffel.UUCP> to the case he mentions. *Repeated* inheritance is the mechanism precisely meant for such cases. If you want to have the same pattern applied to different operations, then you can simply inherit twice or more from the class that describes the common pattern, and use renaming to distinguish between variations on this pattern. Repeated inheritance is discussed in section 11.6 of ``Object-oriented software construction''. Consider a class NEW which inherits from a class ANCESTOR in more than one way, directly or indirectly (e.g. NEW could list ANCESTOR more than once in its own inheritance clause, or it could have two or more parents that are themselves descendants of ANCESTOR.) Consider now a feature f of ANCESTOR. Its status in NEW is determined by the following rule (p. 277 of OOSC): - If f has not been renamed along any of the inheritance paths between NEW and ANCESTOR, then f is considered to represent just one feature of NEW (*sharing* case). - Any renaming of f along any of the inheritance paths yields a separate version of f in NEW (*duplication* case.) Warning: In release 2.1, this is correctly implemented for routines only, not attributes (as explained on p. 97 of the User's Manual). Release 2.2 treats all cases correctly. For the case under discussion only routines are needed, so this is not a problem. This possibility provides the flexibility required by the problem under discussion, as shown by the example below. For simplicity I have written an iterator mechanism in which the iterated action simply identifies itself (``I am variant number n''). First the general iteration mechanism is described by a class GENERAL which, inevitably, is deferred. Note, however, that the iterator routine, called ``iteration'', is *not* deferred. This is typical of the use of partially deferred classes for ``CAPTURING COMMON BEHAVIORS'', emphasized (again) in a previous posting. --------------------------- BEGIN: GENERAL ----------------------------- -- Instances of this class represent objects -- to which an iteration is applicable. -- The iteration scheme and the action -- to be performed at each step -- are left unimplemented; they must -- be filled in by descendants. deferred class GENERAL export action feature action is -- The action to be performed at each iteration step deferred end; -- action advance is -- Move on to next step of iteration deferred end; -- advance start is -- Begin iteration deferred end; -- start over: BOOLEAN is -- Is iteration over? deferred end; -- over iteration is -- Perform a complete iteration do from start until over loop action; advance end end -- iteration end -- class GENERAL --------------------------- END: GENERAL ----------------------------- Next, we have an effective (= non-deferred) class SPECIFIC which, as Mr. Gish suggests, needs two variants of the iteration mechanism, represented by two different implementations of routine ``action''. To achieve this, SPECIFIC inherits twice from GENERAL and ensures that the two variants are renamed differently (action1 and action2). Then each variant is defined to a different algorithm. The commonality of the iterator mechanism remains, however, because routine ``iteration'' has not been renamed and hence is shared. Note the flexibility of the mechanism. In this example, everything is shared except ``action'', which is duplicated. However we could also, if necessary, have duplicated some of the iteration controls (``start'', ``over'', ``advance''), or even ``iteration'' (which could be redefined in one case but not in the other). --------------------------- BEGIN: SPECIFIC ---------------------------- -- Instances of this class represent objects -- to which TWO different iterations are applicable. -- These are accessible through exported routines -- iteration1 and iteration2. -- The iteration scheme is the same, but the action -- to be performed at each step is different. class SPECIFIC export iteration1, iteration2 inherit GENERAL rename action as action1, iteration as iteration1 redefine action1; GENERAL rename action as action2, iteration as iteration2 redefine action2; STD_FILES feature i: INTEGER; -- Iteration control minval: INTEGER is 1; maxval: INTEGER is 5; -- Iteration bounds advance is -- Move on to next step of iteration do i := i+1 end; -- advance action1 is -- Identify the action as number 1 do putstring ("This is action 1 executed for i = "); putint (i); new_line end; -- action1 action2 is -- Identify the action as number 2 do putstring ("This is action 2 executed for i = "); putint (i); new_line end; -- action2 start is -- Begin iteration do i := minval end; -- start over: BOOLEAN is -- Is iteration over? do Result := (i > maxval) end; -- over end -- class SPECIFIC --------------------------- END: SPECIFIC ----------------------------- To exercise the above two classes, we build a small system with the following class, called SYSTEM, as root: --------------------------- BEGIN: SYSTEM ----------------------------- class SYSTEM feature Create is do try end; -- Create try is local s: SPECIFIC do s.Create; s.iteration1; s.iteration2 end -- try end -- class SYSTEM --------------------------- END: GENERAL ----------------------------- We assemble the system (compilation is reproduced below) and execute the resulting executable file, ``system''. Here is the result: --------------------------- BEGIN: EXECUTION --------------------------- This is action 1 executed for i = 1 This is action 1 executed for i = 2 This is action 1 executed for i = 3 This is action 1 executed for i = 4 This is action 1 executed for i = 5 This is action 2 executed for i = 1 This is action 2 executed for i = 2 This is action 2 executed for i = 3 This is action 2 executed for i = 4 This is action 2 executed for i = 5 --------------------------- END: EXECUTION ---------------------------- This is the desired effect. The system's assembly (compilation plus linking) had proceeded as follows. You had typed es system and got the following: --------------------------- BEGIN: SYSTEM ASSEMBLY --------------------- Eiffel configuration manager (version 2.1) Pass 1 on class system Pass 1 on class specific Pass 1 on class general Pass 2 on class system Pass 2 on class general Pass 2 on class specific "specific", 13: Warning: Routine action1 was deferred; no need to include it in redefine clause. "specific", 16: Warning: Routine action2 was deferred; no need to include it in redefine clause. Repeated inheritance - Shared feature: advance (Original feature: advance in class general ) Repeated inheritance - Shared feature: start (Original feature: start in class general ) Repeated inheritance - Shared feature: over (Original feature: over in class general ) Repeated inheritance - Duplicated feature: iteration2 (Original feature: iteration in class general ) Pass 3 on class system Pass 3 on class general Pass 3 on class specific Pass 4 on class system Pass 4 on class general Pass 4 on class specific C-compiling system C-compiling general C-compiling specific Generating and C-compiling temporary main file. Linking system (9 classes) es: System assembly complete --------------------------- END: SYSTEM ASSEMBLY ---------------------- Two comments here: - Uses of repeated inheritance give rise to warnings. I feel uneasy about warnings in general, because of an argument that goes somewhat like this: either there is an error and you cannot compile; or there is no error and you shut up. Pragmatically, however, it seems useful to tell the user something in cases like the one above, where he is using an advanced feature of the language, repeated inheritance. He could be using it inadvertently, in which case the message will serve as an alert; if he is using it on purpose, information on whether a feature is shared or duplicated should be useful for double-checkibg. In subsequent versions of Eiffel (not 2.2) only a short message will be displayed, and the user will be able to click on an ``EXPLAIN'' button to see the detailed messages in another window. - Class SPECIFIC lists action1 and action2 in its redefine subclauses. It is not necessary to explicitly include deferred routines in such clauses, hence the message. But in version 2.1 duplication in repeated inheritance will not work for deferred routines unless you do list them as redefined. This was a bug (blush) which has been corrected for 2.2. If you use the above technique with 2.1, you should include the ``redefine'' for deferred routines duplicated under repeated inheritance, such as action1 and action2, even though they were deferred in the first place. (Also, remember not to try duplication for attributes in 2.1.) -- Bertrand Meyer bertrand@eiffel.com