ajk@wren.cs.rmit.OZ.AU (Alan Kent) (05/07/91)
This may be a naive question, but I dont have a compiler installed here at the moment to experiment with. I am having a little trouble working out how to handle each of the following three cases in Eiffel. Consider three classes A, B and C where (the syntax might not be quite right) class A export f ... class B export f inherit A redefine f ... class C export f inherit B redefine f ... Assume Current is an instance of class C. In feature f of class B I would like to be able to... (1) Call version C of method f (the definition in the class of the object) (2) Call version B of method f (the definition in the current class) (3) Call version A of method f (the parent's definition) As I understand Eiffel, it will always do approach (1). The reason I would like to be able to do (2) is for recursion. I dont want my definition of a recursive algorithm in B to be changed by some decendant class. The only way I can think of doing this is to make f call another feature f_secret where f_secret is not exported (and so can be recursive safely). The reason I would like to be able to do (3) is that my decendant class just wants to add a little bit more to the parent class routine definition. The only way I can see of doing this is by copying the parent routine definition in the child with the few extra lines I want added. In other languages that I know of this is achieved using a keyword like "super". Renaming does not seem to do what I want eg: class C export f, B_f inherit B rename f as B_f as I can no longer invoke version C of feature f when the type is B or A (ie. its no longer a redefine). Am I missing something, in particular with case (3)? Thanks. Alan Kent ajk@goanna.cs.rmit.oz.au
rick@tetrauk.UUCP (Rick Jones) (05/08/91)
In article <5583@goanna.cs.rmit.oz.au> ajk@wren.cs.rmit.OZ.AU (Alan Kent) writes: >This may be a naive question, but I dont have a compiler installed here >at the moment to experiment with. I am having a little trouble working out >how to handle each of the following three cases in Eiffel. > >Consider three classes A, B and C where (the syntax might not be quite right) > class A export f ... > class B export f inherit A redefine f ... > class C export f inherit B redefine f ... > >Assume Current is an instance of class C. In feature f of class B I would >like to be able to... >(1) Call version C of method f (the definition in the class of the object) >(2) Call version B of method f (the definition in the current class) >(3) Call version A of method f (the parent's definition) > >As I understand Eiffel, it will always do approach (1). Correct in the normal case, although I think your terminology is a little confused. Approach (3) is quite easy - so I'll answer that first. This requires one of the less obvious combinations of renaming and redefinition. In class B you must declare: class B inherit A rename f as a_f redefine f The redefinition of f works the same is if the rename were not there, but the rename means that the parent feature is also available under its new name. (this is one of the aspects which is going to be considerably tidied up under version 3 - it's caused much confusion for many people) Thus the new definition of f will be something like: f is do a_f ; -- additional local code end Now the more difficult bit :-) Your problem statement seems to confuse the type of a variable with the type of an object. Take for example: var_b: B ; -- variable of type B var_c: C ; -- variable of type C var_b.Create ; var_c.Create ; -- create objects of the two types (1) var_b.f ; -- applies feature f of class B to object of type B (2) var_c.f ; -- applies feature f of class C to object of type C var_b := var_c ; -- assigns object of type C to variable of type B (3) var_b.f ; -- applies feature f of class B to object of type C In statements (1) & (2), the class of the variable and the object are the same, so the result is clear: in (1) the feature f as coded in class B is called, in (2) the one coded in class C is called. In (3) however, dynamic binding has to be considered. Although the variable is declared of class B, the actual object is type C. The actual feature called is the one which C inherits as f, and redefines. In other words, the code for feature f in class B is never invoked. It is a basic property of Eiffel that a class cannot prevent a descendant from redefining any of its features. This includes non-exported as well as exported ones. There is a presumption that inheritance is used responsibly, and assertions (especially the class invariant, which cannot be overridden, only added to) can trap any gross violations which a descendant may try to perpetrate. One approach to recursion or iteration where the algorithm is always the same, but the actual operation on each loop may need to be different in descendants, is to have the recursion feature call a local routine on each loop. This routine would probably not be exported and can be empty, or even deferred, in the class which defines the recursion. Descendants then only need to define (or redefine) this routine, and it will only be required to deal with one instance on each call. -- Rick Jones, Tetra Ltd. Maidenhead, Berks, UK rick@tetrauk.uucp Any fool can provide a solution - the problem is to understand the problem
sakkinen@jyu.fi (Markku Sakkinen) (05/10/91)
In article <1160@tetrauk.UUCP> rick@tetrauk.UUCP (Rick Jones) writes: >In article <5583@goanna.cs.rmit.oz.au> ajk@wren.cs.rmit.OZ.AU (Alan Kent) writes: >> ... >>Consider three classes A, B and C where (the syntax might not be quite right) >> class A export f ... >> class B export f inherit A redefine f ... >> class C export f inherit B redefine f ... >> >>Assume Current is an instance of class C. In feature f of class B I would >>like to be able to... >>(1) Call version C of method f (the definition in the class of the object) >>(2) Call version B of method f (the definition in the current class) >>(3) Call version A of method f (the parent's definition) >> >>As I understand Eiffel, it will always do approach (1). > >Correct in the normal case, although I think your terminology is a little >confused. Yes, the intent would be clearer if both 'feature f' and 'method f' above were substituted by 'routine f'. >Approach (3) is quite easy - so I'll answer that first. This requires one of >the less obvious combinations of renaming and redefinition. In class B you >must declare: > ... [A good explanation of how (3) is indeed possible, contrary to Kent's doubt.] >Now the more difficult bit :-) Your problem statement seems to confuse the >type of a variable with the type of an object. Take for example: > ... No! Variables were not even mentioned in the original posting. You did not quote the salient paragraph in it: -The reason I would like to be able to do (2) is for recursion. I dont want my -definition of a recursive algorithm in B to be changed by some decendant class. -The only way I can think of doing this is to make f call another feature -f_secret where f_secret is not exported (and so can be recursive safely). This is a perfectly reasonable wish. I regard it as a definite defect in Eiffel (and Smalltalk and many other OOPL's) that there is _no way_ to achieve it. The following part of Jones' posting tells why even the work-around suggested above will not give absolute security - although it will work if all descendant class programmers respect the intent of 'f_secret'. >It is a basic property of Eiffel that a class cannot prevent a descendant from >redefining any of its features. This includes non-exported as well as exported >ones. There is a presumption that inheritance is used responsibly, and >assertions (especially the class invariant, which cannot be overridden, only >added to) can trap any gross violations which a descendant may try to perpetrate. >One approach to recursion or iteration where the algorithm is always the same, >but the actual operation on each loop may need to be different in descendants, >is to have the recursion feature call a local routine on each loop. This >routine would probably not be exported and can be empty, or even deferred, in >the class which defines the recursion. Descendants then only need to define >(or redefine) this routine, and it will only be required to deal with one >instance on each call. This suggestion can be relevant in several situations, but it does not help a bit in the exact problem posed by Kent. Read it as a side note. BTW, even a slight generalisation of the original problem is relevant, and a "perfect" OOPL should be able to support it: In a routine f of class B, invoke different versions of routine g (possibly _different_ from f): (1) from the (most specific) class of the current object, (2) from the same class, (3) from ancestor class. Among well-known OOPL's, the explicit class qualification possibility of C++ allows both (2) and (3). For case (3), it is clumsier than 'super' in Smalltalk and many other languages. The Eiffel way of renaming the ancestor class routine looks even clumsier in simple cases, but is probably better for reuse: e.g. if a routine can be copied (possibly with small modifications) from a class to another, unrelated class. Markku Sakkinen Department of Computer Science and Information Systems University of Jyvaskyla (a's with umlauts) PL 35 SF-40351 Jyvaskyla (umlauts again) Finland SAKKINEN@FINJYU.bitnet (alternative network address)