Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!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: Use deferred routines Message-ID: <114@eiffel.UUCP> Date: 9 Mar 89 18:29:17 GMT References: <112@eiffel.UUCP> Organization: Interactive Software Engineering, Santa Barbara CA Lines: 103 From <112@eiffel.UUCP>, reposted by me but originally from Jean-Michel Cornily (CNET, Lannion, France, uunet!mcvax!cnetlu!cornily): > We would like to apply routines on sets of objects, but there seems to > have problems with types. [We would like to write:] > > class A > feature > > set_do_all (action : X) is > do > from set.start > until set.offright > loop > set.value.action; > set.forth; > end; > end ; -- set_do_all > > end; -- class A > > Our problem here is to determine what is X (the type of action). Is there > a type "routine" ? If not, we cannot define such a routine. To achieve the desired effect, declare ``action'' not as a routine argument which is itself of type ``routine'', but as a routine in class VALTYPE, where VALTYPE is the type of the set elements (that is to say, the type of ``value'' in the extract given). The extract remains exactly as it is, except that the ``action'' formal argument disappears: ------------------------------------------------------------------------ | class A | feature | | set_do_all is | do | -- THE REST AS BEFORE ------------------------------------------------------------------------ For reusability and generality, this scheme must support more than one possible type of ``action'' and more than one possible variant of ``VALTYPE''. To achieve this, declare VALTYPE as a deferred class and ``action'' as one of its deferred routines (a procedure in the example given). Then ``value'' in class A should be declared of type VALTYPE; actual set ``value''s will be references to instances of non-deferred descendants of class VALTYPE. Each such descendant provides its own version of ``action''; dynamic binding ensures that the appropriate version is automatically selected in each case, based on the types of the actual set elements. The cleanest way to proceed is to declare A as a generic class, whose generic parameter is constrained to be a descendant of VALTYPE. This is written as follows (calling the class ACTIVE_SET rather than A): --------------------------------------------------------------------- | class ACTIVE_SET [V -> VALTYPE] export | | ... do_all, ... | | feature | | value: V is ... (could be a function or attribute | depending on the implementation chosen) | | do_all is | -- Apply ``action'' to all set elements | do | from | set.start | until | set.offright | loop | set.value.action; | set.forth; | end; | end; -- do_all | | ... Other features ... | | end -- class ACTIVE_SET --------------------------------------------------------------------- Actual generic parameters provided for V when ACTIVE_SET is actually used are constrained to be descendants of VALTYPE. Constrained genericity is not described in ``Object-Oriented Software Construction'' (although it was more or less announced in exercise 19.5, page 422); the precise specification of the mechanism is given in the Eiffel User's Manual, section 4.8, pages 38-40. As a minor observation about the recommended style for choosing names, it usually preferable *not* to qualify a routine name by the type of objects to which it applies if this type is just the class where the routine appears. In the above case, ``do_all'' (rather than ``set_do_all'') suffices. Any other class (say a LIST) may have a routine with the same name. This is a form of name overloading; it helps provide consistent, mnemonic class interfaces. Bertrand Meyer bertrand@eiffel.com