!standard 8.3.1(0/2) 05-10-24 AC95-00119/01 !class Amendment 05-10-24 !status received no action 05-10-24 !status received 05-09-30 !subject Extending a primitive routine !appendix From: Randy Brukardt Date: Friday, September 30, 2005 7:54 PM After a discussion on comp.lang.ada, I posting this here to put it on the record as an idea for Ada 2019. The discussion was about extending (rather than replacing or inheriting) routines. Several O-O languages (Common Lisp was given as an example) provide some mechanism like that, and it is commonly needed in Ada. For instance, Finalize routines usually need to call their parent when they are completed, so that the parent finalization is accomplished. Failing to do that (or doing it wrong) is a common source of errors in Ada code, and the consequences can remain hidden for a long time. It occurred to me that we already have the needed syntax hooks in Ada 200Y; we're just missing the necessary keywords. We have the "overriding" keyword to specify overriding. Additional keywords could specify other types of extension -- "extends" perhaps. That would look like: extends procedure Finalize (Obj : in out My_Type); or perhaps extends after procedure Finalize (Obj : in out My_Type); to specify when the parent routine is called. The parent routine would be called just as the inherited one would have been at an appropriate place. But I think that we'd only want to support calling the parent routine first or last; special syntax for calling it in the middle hardly seems worth it (and getting the parameters right then would be messy - calling the parent last could be done with jumps, and first probably also could be done with shared code). I'm mildly sorry I didn't think of this when we were working on "overrides"; its a rather natural extension to the idea that also helps to prevent bugs. **************************************************************** From: Tucker Taft Date: Saturday, October 1, 2005 10:30 AM If we really want to prevent bugs, it seems the declaration really belongs on the parent type's routine, rather than the child's routine. Writing "overriding" instead of "extending" seems just as easy a mistake to make as leaving out the call on the parent routine. On the other hand, if the parent routine indicates that it *must* be called at some point from the child's routine, then that would prevent the error from occurring. This makes me think that this would be just the right thing for a pragma. It is relatively rare, and adding it only makes a legal program illegal, rather than vice-versa. Something like "pragma Requires_Inclusion(Finalize);" on the declaration of a primitive would require each descendant that overrides the primitive to include a call on their parent's primitive within the body of the overriding routine, at a point that is executed exactly once on every call that completes normally (i.e. it appears once on every path to a "return", and not inside a loop). **************************************************************** From: Bob Duff Date: Saturday, October 1, 2005 1:30 PM > If we really want to prevent bugs, it seems the declaration > really belongs on the parent type's routine, rather than the > child's routine. Writing "overriding" instead of "extending" > seems just as easy a mistake to make as leaving out the > call on the parent routine. On the other hand, if the parent > routine indicates that it *must* be called at some point from > the child's routine, then that would prevent the error from > occurring. I think that makes sense. But I also think that before any of us starts designing such a feature, we ought to look at how other languages do it. The only one I know of is CLOS (and Lisp Flavors, from whence it came). Any others? And I don't think anybody is in a hurry about this. ;-) > This makes me think that this would be just the right > thing for a pragma. It is relatively rare, and adding it > only makes a legal program illegal, rather than vice-versa. > Something like "pragma Requires_Inclusion(Finalize);" on > the declaration of a primitive would require each descendant > that overrides the primitive to include a call on > their parent's primitive within the body of the overriding > routine, at a point that is executed exactly once on every call > that completes normally (i.e. it appears once on > every path to a "return", and not inside a loop). If we're going to do that, we ought also to change the rules for return statements in functions. Remove the rule requiring at least one return statement. Remove the run-time check for falling off the end. Add a rule saying there must be a return or a raise on every path. Or a call to a pragma No_Return procedure. **************************************************************** From: Florian Weimer Date: Tuesday, October 4, 2005 5:53 PM > But I also think that before any of us starts designing such a feature, > we ought to look at how other languages do it. The only one I know of > is CLOS (and Lisp Flavors, from whence it came). Any others? AOP has picked up some aspects (ahem) of method combination. I like the pragma-based approach. I'm a bit concerned that the "extends" proposal doesn't really fit Ada because there appears to be a tendency in the language to avoid magic calls to user-defined subprograms. **************************************************************** From: Tucker Taft Date: Tuesday, October 4, 2005 6:10 PM A somewhat more pleasant name for the pragma might be "essential". That is: procedure Finalize(X : in out T); pragma Essential(Finalize); This means that this operation is an "essential" primitive of T, i.e. it is part of the "essence" of T, and if it is overridden, it must called from within the overriding, exactly once on every path that returns normally. **************************************************************** From: Thomas Quinot Date: Wednesday, October 5, 2005 3:31 AM > But I think that we'd only want to support calling the parent routine first > or last; special syntax for calling it in the middle hardly seems worth it > (and getting the parameters right then would be messy - calling the parent > last could be done with jumps, and first probably also could be done with > shared code). This seems unnecessarily rigid to me. An alternative might be to provide an attribute allowing the body of a primitive operation to make a reference to the overridden primitive of the ancestor type: procedure Finalize (X : in out T) is begin ... Finalize'Overridden (X); ... end Finalize; This would allow calling the parent operation before, after, in the middle of, or conditionally depending upon, additional processing in the overriding one. **************************************************************** From: Adam Beneschan Date: Wednesday, October 5, 2005 10:19 AM How would this be different from Finalize (Parent_Type(X)); or Parent_Type(X).Finalize; ? I don't think that what's under discussion here is providing a way for overriding routines to call overridden ones. Ada has always had that ability (going back to Ada 83!). I believe we're talking about ways to either make it *mandatory* for an overriding routine to call an overridden one, or providing a way for the compiler to generate such a call automatically. **************************************************************** From: Randy Brukardt Date: Wednesday, October 5, 2005 1:00 PM Correct. The problem is forgetting to call the parent routine, which breaks the abstraction for some operations (like Finalize). Ways to either prevent or detect such a mistake is what we're talking about. **************************************************************** From: Thomas Quinot Date: Thursday, October 6, 2005 2:50 PM The semantics would be exactly the same, but it would be expressed in the derived operation in a way that would be independant of the specific name and location of the parent type. **************************************************************** From: Martin Krischik Date: Friday, October 7, 2005 1:10 AM In C++ they had a similar discussion and in the end they decided not to add a new language feature since the problem can be solved with the features allready there. And it is the same for Ada - only we have two options open: package Inherited renames Parent_Package; or subtype Inherited is Parent_Package.Parent_Type; Depending if you want call Package.Operator (Type) or Type.Operator. The first option was even possible in Ada 95. In C++ they have added a declaration like this into the private part of every class. **************************************************************** From: Randy Brukardt Date: Monday, October 24, 2005 8:50 PM > If we really want to prevent bugs, it seems the declaration > really belongs on the parent type's routine, rather than the > child's routine. Writing "overriding" instead of "extending" > seems just as easy a mistake to make as leaving out the > call on the parent routine. On the other hand, if the parent > routine indicates that it *must* be called at some point from > the child's routine, then that would prevent the error from > occurring. The "bugs" I was interested in preventing was writing the call incorrectly, or failing to put it on every path. That's why I wanted it included automatically. I'm not much interested in "forcing" the call to be included, because there are cases where you might not want to actually call the parent routine. It's better to let the client programmer decide that. Moreover, its writing the call itself that is error-prone, not just the omission of it. > This makes me think that this would be just the right > thing for a pragma. It is relatively rare, and adding it > only makes a legal program illegal, rather than vice-versa. > Something like "pragma Requires_Inclusion(Finalize);" on > the declaration of a primitive would require each descendant > that overrides the primitive to include a call on > their parent's primitive within the body of the overriding > routine, at a point that is executed exactly once on every call > that completes normally (i.e. it appears once on > every path to a "return", and not inside a loop). Determining that an explicitly written call is on the right path, is a call to the parent operation, and has the same parameters, seems like a rather difficult thing to do. It would seem to me to be very much like the return statement fiasco. Florian Weimer wrote: > I'm a bit concerned that the > "extends" proposal doesn't really fit Ada because there appears to be > a tendency in the language to avoid magic calls to user-defined > subprograms. You haven't done much programming with controlled types, I take it. ;-) There's a lot of magic calls there. I'm primarily interested in having a way to automatically call the parent routine (without the complication of writing it, with all of the possibilities for error that entails, or insuring its on all of the possible paths). A more flexible and more explicit alternative would some syntax that stands in for a call to the parent routine. Say: 'Parent(<>) (The box would be a stand-in for the parameter list, which would be passed unmodified. Writing the parameter list is the worst cause of errors.) In any case, it doesn't seem that we even have an agreement on what the problem is, so it's pretty clear that we can't design a solution. ****************************************************************