* How to write TYPECASE in Ada 95? @ 1999-02-05 0:00 Norman Ramsey 1999-02-05 0:00 ` Brian Rogoff ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Norman Ramsey @ 1999-02-05 0:00 UTC (permalink / raw) I'm trying to figure out from the reference manual how to identify which type extension of a type I have. For example, if I have type Point is tagged record X, Y : Real := 0.0; end record; type Painted_Point is new Point with record Paint : Color := White; end record; type Tall_Point is new Point with record Height : Real := 0.0; end record; I'd like to be able to discriminate at run time: p : Point; ... if p in Painted_Point then ... do something with Painted_Point'(p) else if p in Tall_Point then ... do something with Tall_Point'(p) Even better would be something like Modula-3 TYPECASE: (* modula-3 *) TYPECASE p OF Painted_Point (painted) => ... do something with painted Tall_Point (tail) => ... do something with tall ... END What's the idiomatic way to express this in Ada? Norman -- Norman Ramsey http://www.cs.virginia.edu/~nr ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 How to write TYPECASE in Ada 95? Norman Ramsey @ 1999-02-05 0:00 ` Brian Rogoff 1999-02-05 0:00 ` David C. Hoos, Sr. 1999-02-06 0:00 ` Ed Falis 1999-02-06 0:00 ` How to write TYPECASE in Ada 95? Matthew Heaney 1999-02-06 0:00 ` David C. Hoos, Sr. 2 siblings, 2 replies; 34+ messages in thread From: Brian Rogoff @ 1999-02-05 0:00 UTC (permalink / raw) On 5 Feb 1999, Norman Ramsey wrote: > > I'm trying to figure out from the reference manual how to identify > which type extension of a type I have. For example, if I have > > type Point is tagged > record > X, Y : Real := 0.0; > end record; > > type Painted_Point is new Point with > record > Paint : Color := White; > end record; > > > type Tall_Point is new Point with > record > Height : Real := 0.0; > end record; > > I'd like to be able to discriminate at run time: > > p : Point; > > ... > > if p in Painted_Point then > ... do something with Painted_Point'(p) > else if p in Tall_Point then > ... do something with Tall_Point'(p) > > Even better would be something like Modula-3 TYPECASE: > > (* modula-3 *) > TYPECASE p OF > Painted_Point (painted) => ... do something with painted > Tall_Point (tail) => ... do something with tall > ... > END > > What's the idiomatic way to express this in Ada? There is no typecase, but if you want exact tag comparisons you can use if p'Tag = Painted_Point then ... do someting with Painted_Point elsif p'Tag = Tall_Point then ... yadda yadda yadda ... -- Brian ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 ` Brian Rogoff @ 1999-02-05 0:00 ` David C. Hoos, Sr. 1999-02-05 0:00 ` Brian Rogoff 1999-02-06 0:00 ` Ed Falis 1 sibling, 1 reply; 34+ messages in thread From: David C. Hoos, Sr. @ 1999-02-05 0:00 UTC (permalink / raw) Brian Rogoff wrote in message ... >On 5 Feb 1999, Norman Ramsey wrote: <large snip> >There is no typecase, but if you want exact tag comparisons you can >use > > if p'Tag = Painted_Point then > ... do someting with Painted_Point > elsif p'Tag = Tall_Point then > ... yadda yadda yadda > ... Don't you mean: if P'Tag = Painted_Point'Tag then ... do someting with Painted_Point elsif P'Tag = Tall_Point'Tag then ... yadda yadda yadda ... ?? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 ` David C. Hoos, Sr. @ 1999-02-05 0:00 ` Brian Rogoff 0 siblings, 0 replies; 34+ messages in thread From: Brian Rogoff @ 1999-02-05 0:00 UTC (permalink / raw) On Fri, 5 Feb 1999, David C. Hoos, Sr. wrote: > Brian Rogoff wrote in message ... > >On 5 Feb 1999, Norman Ramsey wrote: > <large snip> > >There is no typecase, but if you want exact tag comparisons you can > >use > > > > if p'Tag = Painted_Point then > > ... do someting with Painted_Point > > elsif p'Tag = Tall_Point then > > ... yadda yadda yadda > > ... > Don't you mean: > > if P'Tag = Painted_Point'Tag then > ... do someting with Painted_Point > elsif P'Tag = Tall_Point'Tag then > ... yadda yadda yadda > ... > ?? Doh! Yes, of course you are correct. Thanks for pointing this out. -- Brian ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 ` Brian Rogoff 1999-02-05 0:00 ` David C. Hoos, Sr. @ 1999-02-06 0:00 ` Ed Falis 1999-02-06 0:00 ` Nick Roberts ` (2 more replies) 1 sibling, 3 replies; 34+ messages in thread From: Ed Falis @ 1999-02-06 0:00 UTC (permalink / raw) > On 5 Feb 1999, Norman Ramsey wrote: > > > > I'm trying to figure out from the reference manual how to identify > > which type extension of a type I have. For example, if I have > > > > > I'd like to be able to discriminate at run time: > > > > p : Point; > > > > ... > > > > if p in Painted_Point then > > ... do something with Painted_Point'(p) > > else if p in Tall_Point then > > ... do something with Tall_Point'(p) > > > > Even better would be something like Modula-3 TYPECASE: > > > > (* modula-3 *) > > TYPECASE p OF > > Painted_Point (painted) => ... do something with painted > > Tall_Point (tail) => ... do something with tall > > ... > > END > > > > What's the idiomatic way to express this in Ada? I thought a good part of the point of tagged types (and support for polymorphism in other OOP's) was exactly to avoid this kind of code. - Ed ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Ed Falis @ 1999-02-06 0:00 ` Nick Roberts 1999-02-06 0:00 ` Nick Roberts 1999-02-17 0:00 ` Tom Moran 1999-02-19 0:00 ` Tom Moran 2 siblings, 1 reply; 34+ messages in thread From: Nick Roberts @ 1999-02-06 0:00 UTC (permalink / raw) Given type Root_Type is abstract tagged ...; and type Variation_1 is new Root_Type with ...; type Variation_2 is new Root_Type with ...; and you have a particular 'something' you might want to do to an object which could be either of type Variation_1 or Variation_2, you might find it clearest to create an abstract procedure procedure Something (X: Root_Type; ...) is abstract; and then override this procedure with procedure Something (X: Variation_1; ...) ... whose body does what is appropriate for objects of type Variation_1, and then procedure Something (X: Variation_2; ...) ... whose body does what is appropriate for objects of type Variation_2. A call such as Something(A,...); will then automatically call the correct version of Something, based on the type of object A. This is called 'dispatching'. Dispatching generally obviates any need for a TYPECASE construct. However, it is sometimes the case that all those separate procedures would be overkill. Given type Subvariation_1_1 is new Variation_1 with ...; type Subvariation_1_2 is new Variation_1 with ...; you may have a piece of code which operates on objects of type Variation_1, but has an extra operation, somewhere in the middle, necessary for objects of type Subvariation_1_2 (say). In these cases, it is more sensible to use the 'in' operator to factor out this snippet of code, e.g. ... if X in Subvariation_1_2'Class then ... end if; ... You should (almost) always test for membership of a class. E.g. test "X in Subvariation_1_2'Class" rather than "X in Subvariation_1_2", so that further descendants of Subvariation_1_2 (if you should add any at a later date) will still be catered for. Use the 'in' operator, not 'Tag. The need to use 'Tag is very rare (and always to do with I/O or interfacing). ------------------------------------------- Nick Roberts ------------------------------------------- ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Nick Roberts @ 1999-02-06 0:00 ` Nick Roberts 0 siblings, 0 replies; 34+ messages in thread From: Nick Roberts @ 1999-02-06 0:00 UTC (permalink / raw) To be finicky, I should have said, for the example call Something(A,...); that if A has a static type, the correct Something is selected statically as a part of normal overload resolution. It is only if A has a dynamic type (i.e. it is an object of a class-wide type) that the dynamic selection of Something occurs. It is only the dynamic selection which is called 'dispatching'. ------------------------------------------- Nick Roberts ------------------------------------------- ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Ed Falis 1999-02-06 0:00 ` Nick Roberts @ 1999-02-17 0:00 ` Tom Moran 1999-02-18 0:00 ` Matthew Heaney 1999-02-19 0:00 ` Tom Moran 2 siblings, 1 reply; 34+ messages in thread From: Tom Moran @ 1999-02-17 0:00 UTC (permalink / raw) Dispatching is certainly better than a case statement - if you use it. Consider an existing package of the form: package Grocer is type Fruit is abstract tagged ... type Apple is new Fruit ... ... type Watermelon is new Fruit ... ... function Best(Budget : in Money) return Fruit'class; end Grocer; And now you are writing a new package which, among other things, does with Grocer; use Grocer; package Meal is procedure Serve(Dish : in Apple); ... procedure Serve(Dish : in Watermelon); end Meal; Suppose a user wants, not unreasonably, to write ... Meal.Serve(Dish => Grocer.Best(Budget=>1.00)); Unless you can go back and modify Grocer, it appears you must include in Meal procedure Serve_Fruit(Dish : in Fruit'class); with a body like procedure Serve_Fruit(Dish : in Fruit'class) is begin if Dish in Apple'class then Serve(Apple(Dish)); ... elsif Dish in Watermelon'class then Serve(Watermelon(Dish)); else raise Heck; end if; end Serve_Fruit; Is there a better way? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-17 0:00 ` Tom Moran @ 1999-02-18 0:00 ` Matthew Heaney 1999-02-18 0:00 ` robert_dewar ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-18 0:00 UTC (permalink / raw) tmoran@bix.com (Tom Moran) writes: > Dispatching is certainly better than a case statement - if you use it. > > Consider an existing package of the form: > > package Grocer is > type Fruit is abstract tagged ... > type Apple is new Fruit ... > ... > type Watermelon is new Fruit ... > ... > function Best(Budget : in Money) return Fruit'class; > end Grocer; > > And now you are writing a new package which, among other things, does > > with Grocer; > use Grocer; > package Meal is > procedure Serve(Dish : in Apple); > ... > procedure Serve(Dish : in Watermelon); > end Meal; > > Suppose a user wants, not unreasonably, to write > ... > Meal.Serve(Dish => Grocer.Best(Budget=>1.00)); > > Unless you can go back and modify Grocer, it appears you must include > in Meal > procedure Serve_Fruit(Dish : in Fruit'class); > with a body like > procedure Serve_Fruit(Dish : in Fruit'class) is > begin > if Dish in Apple'class then Serve(Apple(Dish)); > ... > elsif Dish in Watermelon'class then Serve(Watermelon(Dish)); > else raise Heck; > end if; > end Serve_Fruit; > > Is there a better way? It seems like the real problem in your example is that Fruit doesn't include any primitive operations for Serve'ing. If you want to extend a type with arbitrary behavior, you could take a look at using the Visitor pattern. Ada implementations of this pattern are available at the ACM patterns archive. <http://www.acm.org/archives/patterns.html> ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` Matthew Heaney @ 1999-02-18 0:00 ` robert_dewar 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Nick Roberts 1999-02-18 0:00 ` Tom Moran 1999-02-18 0:00 ` Tom Moran 2 siblings, 2 replies; 34+ messages in thread From: robert_dewar @ 1999-02-18 0:00 UTC (permalink / raw) tmoran@bix.com (Tom Moran) writes: > Dispatching is certainly better than a case statement - > if you use it. I strongly disagree with this statement. This particular bit of conventional wisdom is badly in error, and can lead to unnecessarily obscure hard to maintain programs. Case statements and dispatching represent two ways of slicing a 2-D array of types vs operations. The use of case statements may make it easier to add operations, the use of dispatching may make it easier to add types. Which is better depends on the particular situation. -----------== Posted via Deja News, The Discussion Network ==---------- http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` robert_dewar @ 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Nick Roberts 1 sibling, 0 replies; 34+ messages in thread From: Tom Moran @ 1999-02-19 0:00 UTC (permalink / raw) >The use of >case statements may make it easier to add operations, the >use of dispatching may make it easier to add types. Which >is better depends on the particular situation. And what do you suggest for the case where an existing system has been designed with tagged records to make it easy to add types, and you find it necessary to add an operation? Must one then add a typecase, pardon me, an if-elsif series of "if x in variant_1 then..."? Or is there a better way (short of modifying the original system, of course)? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` robert_dewar 1999-02-19 0:00 ` Tom Moran @ 1999-02-19 0:00 ` Nick Roberts 1 sibling, 0 replies; 34+ messages in thread From: Nick Roberts @ 1999-02-19 0:00 UTC (permalink / raw) robert_dewar@my-dejanews.com wrote in message <7ai3m2$4t0$1@nnrp1.dejanews.com>... |tmoran@bix.com (Tom Moran) writes: |> Dispatching is certainly better than a case statement - |> if you use it. | |I strongly disagree with this statement. This particular |bit of conventional wisdom is badly in error, and can lead |to unnecessarily obscure hard to maintain programs. | |Case statements and dispatching represent two ways of |slicing a 2-D array of types vs operations. The use of |case statements may make it easier to add operations, the |use of dispatching may make it easier to add types. Which |is better depends on the particular situation. I believe I illustrated in a previous post the reason why using dispatching is sometimes 'overkill'. To recap briefly, imagine a large tree of types: T; Ta; Tb; Taa; Tab; Tba; Tbb; Taaa; Taab; Taba; Tabb; and so on. T is at the top. Ta and Tb are derived from T. Taa and Tab are derived from Ta. You get the picture. Now imagine a procedure P(X: in out T'Class) which contains a large number of lines of processing, and which has a slight variation in the middle just for the types Tabaab and Tbbaba, say. It only needs an 'if' or 'case', in the appropriate place, to distinguish these types and do the special actions for them. Now consider, instead, having P(X: in out T), and then having to overload this procedure for each and every type Ta, Tb, Taa, etc., etc.. Lots of types times many lines of code, most of which are all the same. Disastrous. Even if the fixed parts are factored out, you still have to declare lots of overloadings; it's just not worth it! Finally, consider a procedure Q(X: in out T) which performs an operation that has a substantially different implementation for each and every different type. Dispatching for procedure Q makes a lot of sense. Dispatching is very often an excellent approach. But, it is definitely not _always_ the best solution! ------------------------------------- Nick Roberts 'The time has come,' the Walrus said, 'To talk of many things: Of shoes--of ships--and sealing wax-- Of cabbages--and kings-- And why the sea is boiling hot-- And whether pigs have wings.' Lewis Carroll "Through the Looking Glass" ------------------------------------- ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` Matthew Heaney 1999-02-18 0:00 ` robert_dewar @ 1999-02-18 0:00 ` Tom Moran 1999-02-18 0:00 ` Tom Moran 2 siblings, 0 replies; 34+ messages in thread From: Tom Moran @ 1999-02-18 0:00 UTC (permalink / raw) >> Unless you can go back and modify Grocer, >It seems like the real problem in your example is that Fruit doesn't >include any primitive operations for Serve'ing. Right. >If you want to extend a type with arbitrary behavior, you could take a >look at using the Visitor pattern. Ada implementations of this pattern >are available at the ACM patterns archive. ><http://www.acm.org/archives/patterns.html> Thanks, I'll take a look there. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` Matthew Heaney 1999-02-18 0:00 ` robert_dewar 1999-02-18 0:00 ` Tom Moran @ 1999-02-18 0:00 ` Tom Moran 1999-02-18 0:00 ` Matthew Heaney 2 siblings, 1 reply; 34+ messages in thread From: Tom Moran @ 1999-02-18 0:00 UTC (permalink / raw) >If you want to extend a type with arbitrary behavior, you could take a >look at using the Visitor pattern. Ada implementations of this pattern >are available at the ACM patterns archive. ><http://www.acm.org/archives/patterns.html> Am I looking at the right place? I see >package body Equipment.Pricing is > function Get_Total_Price > (Equipment : Root_Equipment'Class) return Dollars is > begin > if Equipment in Floppy_Disk then > declare > Disk : Floppy_Disk renames Floppy_Disk (Equipment); > begin > return Get_Price_Of_Floppy_Disk (Disk); > end; > elsif Equipment in Hard_Disk then > declare > Disk : Hard_Disk renames Hard_Disk (Equipment); > begin > return Get_Price_Of_Hard_Disk (Disk); > end; which surely looks pretty similar to > procedure Serve_Fruit(Dish : in Fruit'class) is > begin > if Dish in Apple'class then Serve(Apple(Dish)); > ... > elsif Dish in Watermelon'class then Serve(Watermelon(Dish)); ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-18 0:00 ` Tom Moran @ 1999-02-18 0:00 ` Matthew Heaney 0 siblings, 0 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-18 0:00 UTC (permalink / raw) tmoran@bix.com (Tom Moran) writes: > >If you want to extend a type with arbitrary behavior, you could take a > >look at using the Visitor pattern. Ada implementations of this pattern > >are available at the ACM patterns archive. > > ><http://www.acm.org/archives/patterns.html> > Am I looking at the right place? I see > > >package body Equipment.Pricing is > > > > function Get_Total_Price > > (Equipment : Root_Equipment'Class) return Dollars is > > begin > > if Equipment in Floppy_Disk then ... > which surely looks pretty similar to > > > procedure Serve_Fruit(Dish : in Fruit'class) is > > begin > > if Dish in Apple'class then Serve(Apple(Dish)); > > ... > > elsif Dish in Watermelon'class then Serve(Watermelon(Dish)); Indeed, you are correct. The was the last of 3 versions of the visitor pattern I sent to the list, and I discuss the pros and cons of each approach in the accompanying text. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Ed Falis 1999-02-06 0:00 ` Nick Roberts 1999-02-17 0:00 ` Tom Moran @ 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Tom Moran 1999-02-23 0:00 ` Samuel Mize 2 siblings, 2 replies; 34+ messages in thread From: Tom Moran @ 1999-02-19 0:00 UTC (permalink / raw) >>> I thought a good part of the point of tagged types >>> (and support for polymorphism in other OOP's) was >>> exactly to avoid this kind of code. (series of if-elsif) >>Dispatching is certainly better than a case statement - if you use it. >>Consider an existing package of the form: (example of dispatching being inappropriate) >tmoran@bix.com (Tom Moran) writes: >> Dispatching is certainly better than a case statement - >> if you use it. >I strongly disagree with this statement. This particular >bit of conventional wisdom is badly in error, and can lead >to unnecessarily obscure hard to maintain programs. >Case statements and dispatching represent two ways of I see you strongly agree that dispatching is not always the solution, and a case statement, for instance, is more appropriate. But in the situation at hand, the only thing apparently available is the original poster's original if-elsif list, with the usual hazards of left out or overlapping cases. Was something like Modula 3's "Typecase" considered for Ada 95? Are there problems with it? ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-19 0:00 ` Tom Moran @ 1999-02-19 0:00 ` Tom Moran 1999-02-23 0:00 ` Samuel Mize 1 sibling, 0 replies; 34+ messages in thread From: Tom Moran @ 1999-02-19 0:00 UTC (permalink / raw) Sorry, the above was a response to Dewar, not Falis. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Tom Moran @ 1999-02-23 0:00 ` Samuel Mize 1999-02-23 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Mike Silva 1 sibling, 1 reply; 34+ messages in thread From: Samuel Mize @ 1999-02-23 0:00 UTC (permalink / raw) Tom Moran <tmoran@bix.com> wrote: > But in the situation at hand, the only thing apparently available is > the original poster's original if-elsif list, with the usual hazards > of left out or overlapping cases. Was something like Modula 3's > "Typecase" considered for Ada 95? Are there problems with it? I've waited a couple of days, and more knowledgeable people haven't replied. Hopefully they'll correct any errors in the following. (Often the best way to get info on the net is not to post a question, but to post a wrong answer...) Anyway, here's my understanding. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A "typecase" type of semantic was indeed considered for Ada 95. There are four problems with a "typecase" capability, from an Ada point of view. The first three consider the meaning of the constructs involved; the last considers the efficiency and optimization of these constructs. 1. Since you cannot check for complete and non-overlapping coverage, the "typecase" statement adds no advantage over an "if-elsif" cascade. It's appealing to think that you could check coverage of the class hierarchy, but you couldn't. Since a class can be added by the later compilation of a library package, you cannot check for complete coverage of all tags in the hierarchy. Nor can you usefully check for non-overlapping coverage in most cases. If the root type of the hierarchy is not abstract, it must be covered. Non-overlapping coverage would then keep you from adding any other tags, and you'd have a block instead of a case statement. Even if your class hierarchy is abstract down to some level, it greatly reduces the utility of a "typecase" construct to prevent overlapping classes. If one type is a special case, you must call it out. You then cannot handle its parent type's (overlapping) class as a general case. So, if a library package later extends that parent type, your typecase will consider the extended type to be an "others" case, not a member of the parent class. So a "typecase" construct can add no semantic value over an "if-elsif" cascade. It would just be "syntactic sugar" -- a convenience, but no real addition to the expressive power of the language. And, it would give the unsophisticated user a deceptive appearance of checking class coverage. 2. Adding a "typecase" meaning to "case" would alter the underlying meaning of "case". A "case" statement enumerates all the possible options that it covers -- it lists them out explicitly, so a later programmer can see what they are. "Others" is allowed only because you can enumerate out what it means if you need to. This is not possible for a typecase statement. A new type can be added by any arbitrary package, later. So, an extended "case" for tags would have a different basic meaning than any other "case" statement. This would be confusing. Of course, this point only argues against extending "case," not against adding a new construct. 3. A "typecase" would encourage dangerous usage in the face of programming by extension. Remember, tagged types are NOT a facility for object-oriented programming. Together with other facilities in the language, they support that idiom. However, tagged types are a facility for programming by extension, in a much more general form than many object-oriented languages provide. Ideally, whenever a tagged type is extended, you could add whatever code changes are needed to accomodate it at the location of the extension. No changes in the pre-existing system would be needed. If there are class-wide operations that may be applied to the new type, this is not the case. These must be checked to ensure that they are still correct. If, internally, such an operation branches on a tag value, the pre-existing system may need to be altered -- and the attempt to program by extension has, to some extent, failed. This is especially a problem if the body of the class-wide operation is hidden from the programmer, for instance as part of a proprietary package for which only the spec is provided. So branching on tag values is dangerous -- although not dangerous enough to merit an "Unchecked_" prefix. Adding a "typecase" would implicitly condone widespread use of this idiom, which should only be used after careful consideration of other alternatives, and of the risk involved. (Which is not to say it shouldn't be used.) 4. It would take sophisticated compiler support to make a "typecase" efficient. Since both the "case" statement and tagged types are intended to be very efficient, this could be a damaging surprise to the user. "Case" should just compute the expression and do an indirect jump. Tagged types should dispatch very fast with a jump table. But the combination of these two would require an arbitrarily-long search of the class hierarchy. If this occurs in a tight "heartbeat" loop, it may be a significant performance issue. Now it seems to me (without deep analysis, I admit) that a clever compiler suite could build up a static jump table for each case statement at build time, once it knew what tags would have to be considered. This could run pretty efficiently, and shouldn't take up *too* much space. But, the Ada 95 team was weighing a lot of alternatives, many of which require quite clever compilers. I assume that this was not considered important enough to merit such a requirement for cleverness. In particular, I doubt that they wanted to add a requirement for the binder to generate code, and without such fancy-and-late code generation a "typecase" would be surprisingly slow. Worse, its slowness would vary radically between these two classes of compiler. Use of a separate keyword like "typecase" might reduce the surprise issue. On the other hand, many programmers would expect such a construct to be similar to "case" and would not delve deeply into the language design issues. And, the Ada 95 language designers tried to keep new keywords to a minimum, so they had to add something really important to be considered. Best, Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam ^ permalink raw reply [flat|nested] 34+ messages in thread
* Question (was Re: How to write TYPECASE in Ada 95?) 1999-02-23 0:00 ` Samuel Mize @ 1999-02-23 0:00 ` Mike Silva 1999-02-24 0:00 ` Samuel T. Harris ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Mike Silva @ 1999-02-23 0:00 UTC (permalink / raw) Samuel Mize wrote in message <7av52o$62g@news3.newsguy.com>... <...> >Remember, tagged types are NOT a facility for object-oriented >programming. Together with other facilities in the language, they >support that idiom. However, tagged types are a facility for >programming by extension, in a much more general form than many >object-oriented languages provide. This intrigues me greatly, but being an Ada neophyte and not having that much OOP background in general I don't know what it means. Would anybody care to offer an explanation, or point me in the direction of one. Thanks much. Mike Silva ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: Question (was Re: How to write TYPECASE in Ada 95?) 1999-02-23 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Mike Silva @ 1999-02-24 0:00 ` Samuel T. Harris 1999-02-24 0:00 ` Matthew Heaney 1999-02-24 0:00 ` (long) programming by extension (was: " Samuel Mize 1999-02-24 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Nick Roberts 2 siblings, 1 reply; 34+ messages in thread From: Samuel T. Harris @ 1999-02-24 0:00 UTC (permalink / raw) Mike Silva wrote: > > Samuel Mize wrote in message <7av52o$62g@news3.newsguy.com>... > <...> > >Remember, tagged types are NOT a facility for object-oriented > >programming. Together with other facilities in the language, they > >support that idiom. However, tagged types are a facility for > >programming by extension, in a much more general form than many > >object-oriented languages provide. > > This intrigues me greatly, but being an Ada neophyte and not having that > much OOP background in general I don't know what it means. Would anybody > care to offer an explanation, or point me in the direction of one. Thanks > much. > > Mike Silva One of the attractive attributes of Ada is that facilities are provided without attaching some taxonomy to the facility. Frequently, a combination of facilities are required to match a particular paradim. While this may appear to some as form of "weak" support for that paradim, I see it as freeing the language from the paradim du jour. I am free to combine language elements to suit the mind set at hand without fighting an ingrained taxonomy within the language itself. This also enables the language to keep the features orthogonal to each other. Because so many features and constructions are available, their use together many times calls for what appear to be archane rules to protect the system being developed. The protection is desirable, but the orthogonality of the constructions sometimes hides the necessity of some of the rules. If I was doing structured programming, I'd have a hard time getting past the taxonomy enforced by C++ and other OOLs. I don't have that problem with Ada. As Samuel Mize says, tagged types provided a mechanism for type extension. Since OOP relies upon type extension, tagged types are a natural match. Packages provide encapsulation and naming scope. Packages group things together. A OO class is a grouping of a data structure type and its defined operations. So a class must be a package defining a tagged type and including all the primative operations on that type. Both are required to implement a class. Singular inheritance is directly supported by the language through tagged types. Multiple inheritance, in several forms, is supported by combining tagged types with other language constructions such as generics. I believe Ada 95 Rational provides an in-depth discussion on kinds multiple inheritance, why they are not directly supported, and how achieve the desired result. I just wish Ada didn't use the term class-wide type. It is a term with too much overloaded meaning. Since you are an Ada neophyte, I recommand you visit www.adahome.com and browse around. You'll want to consider downloading the GNAT compiler for your personal use at home. You may also consider getting the Walnut Creek Ada CDROM which contains tons of code and supporting documents which are not just limited to Ada, but also include good software engineering as well. -- Samuel T. Harris, Principal Engineer Raytheon, Scientific and Technical Systems "If you can make it, We can fake it!" ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: Question (was Re: How to write TYPECASE in Ada 95?) 1999-02-24 0:00 ` Samuel T. Harris @ 1999-02-24 0:00 ` Matthew Heaney 1999-02-24 0:00 ` Tucker Taft 0 siblings, 1 reply; 34+ messages in thread From: Matthew Heaney @ 1999-02-24 0:00 UTC (permalink / raw) "Samuel T. Harris" <sam_harris@hso.link.com> writes: > I just wish Ada didn't use the term class-wide type. > It is a term with too much overloaded meaning. The term "class" already had that meaning in Ada83: the class of float point types, the class of integer types, etc. It was Tucker's vision for Ada95 that it preserve as much as possible that Ada83 way of doing things. I just wish Ada allowed the attribute T'Class for non-tagged types too. That would be a way to solve the problem of simultaneous derivation. For example, if New_Line had been declared something like this: procedure New_Line (File : in File_Type; Spacing : in Positive_Count'Class := 1); then if we did this: package P is type File_Type is new Text_IO.File_Type; type Positive_Count is new Text_IO.Positive_Count; end P; then you'd get a New_Line at the point of declaration of P.File_Type that works with type P.Positive_Count. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: Question (was Re: How to write TYPECASE in Ada 95?) 1999-02-24 0:00 ` Matthew Heaney @ 1999-02-24 0:00 ` Tucker Taft 0 siblings, 0 replies; 34+ messages in thread From: Tucker Taft @ 1999-02-24 0:00 UTC (permalink / raw) Matthew Heaney (matthew_heaney@acm.org) wrote: : ... : I just wish Ada allowed the attribute T'Class for non-tagged types too. We had those for a while during the 9X process, but they were considered a bit too "radical" ;-). They survived as the "universal types" for numeric types, but the more general notion of <non-tagged>'Class didn't make the "cut." -- -Tucker Taft stt@averstar.com http://www.averstar.com/~stt/ Technical Director, Distributed IT Solutions (www.averstar.com/tools) AverStar (formerly Intermetrics, Inc.) Burlington, MA USA ^ permalink raw reply [flat|nested] 34+ messages in thread
* (long) programming by extension (was: How to write TYPECASE in Ada 95?) 1999-02-23 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Mike Silva 1999-02-24 0:00 ` Samuel T. Harris @ 1999-02-24 0:00 ` Samuel Mize 1999-02-24 0:00 ` (long) programming by extension Samuel Mize 1999-02-24 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Nick Roberts 2 siblings, 1 reply; 34+ messages in thread From: Samuel Mize @ 1999-02-24 0:00 UTC (permalink / raw) Mike Silva <mjsilva@jps.net> wrote: > > Samuel Mize wrote in message <7av52o$62g@news3.newsguy.com>... > <...> >>Remember, tagged types are NOT a facility for object-oriented >>programming. Together with other facilities in the language, they >>support that idiom. However, tagged types are a facility for >>programming by extension, in a much more general form than many >>object-oriented languages provide. > > This intrigues me greatly, but being an Ada neophyte and not having that > much OOP background in general I don't know what it means. Would anybody > care to offer an explanation, or point me in the direction of one. Thanks > much. I don't have an appropriate pointer to an existing discussion (I'd be interested to see them too). Instead of replying by email, I'll be long-winded here on the net, so others can chime in with additions or corrections. Note that the early part of this covers the basics of defining abstractions in functional programming, to motivate building up to programming by extension toward the end. - - - - - Programming by Extension means programming a large system so that new processing cases can be handled by adding new code, without having to recode any existing modules. Language support is extremely helpful for true programming by extension. Object-oriented development is a very specific way of analyzing, designing and coding a system. It provides a way to program by extension -- that's one of its selling points -- but it brings along a lot of other baggage too. Sometimes you want that baggage. That's why "object-oriented" technology exists. But sometimes you just want to do plain functional programming, and be able to extend the system later without having to recode the existing part. Ada supports this also. The Ada 95 Rationale describes how Ada supports programming by extension, but it doesn't really define the term. Here's a concrete example of programming by extension. I'll motivate it by showing the intermediate manual steps that lead to programming by extension. Consider a windowing user interface. You have a main loop that looks at events, like mouse clicks, and calls the appropriate routine to handle the event, passing that routine a pointer to the window that owns the part of the screen where the event occurred. The simple-minded approach would be to put all the code into the loop. Applying functional abstraction, we can define some procedures like Draw and Process_Mouse_Click, and call those procedures in the event loop. Now, if we put them into a separate package, we can change the procedures without having to recode or recompile the loop: package Windows is type Window is ... procedure Draw (W: Window); procedure Process_Mouse_Click (W: Window); end Windows; - - - - - - with Windows; procedure Event_Loop is W: Windows.Window; E: Event; -- defined somewhere else begin loop Get_Event (E); Get_Current_Window (W); case E is when Draw_Window => Draw (W); when Click => Process_Mouse_Click (W); end case; end loop; end Event_Loop; However, if you add a new kind of window, you will change the Window type. That means you will have to recompile the event loop, and you will have to recode all the procedures in package Windows. That's not a big deal for this small, trivial example, but in a large system it can be a significant cost. Note that Ada is often used in life-critical systems like avionics, where each unit goes through a long and expensive validation of the compiled object code. This must be redone if the unit is recompiled, even if the source code is not changed. So, we want to completely isolate the event loop. We can do this by making Window a pointer that can point at any kind of window, so that Event_Loop won't have to be recompiled when we add a new kind of window. In C, you would do that by declaring Window as a pointer to void. The actual window data structures would all start with an integer. The window procedures, like Draw, would typecast that to pointer-to-integer to see the integer, and use that integer to pick the right type to cast the pointer to in order to handle the event. This is (very) roughly how the X-windows system handles windows. You can do the same thing in Ada 95 or C++, but it's wiser to use the language-provided features. I'll describe Ada's in a moment. And even then, you'll still have a big case statement full of type-specific code in each window routine. The next step is to make the routines in Windows nothing BUT the case statement, and extract all the code out to type-specific packages for each window type. Something like: package Basic_Window is type Window is record Kind: Integer := 1; ... end record; procedure Draw (W: Window); procedure Process_Mouse_Click (W: Window); end Basic_Window; - - - - - - package Alert_Window is -- border flashes red type Window is record Kind: Integer := 2; ... end record; procedure Draw (W: Window); procedure Process_Mouse_Click (W: Window); end Alert_Window; - - - - - - package Windows is type Window is private; procedure Draw (W: Window); procedure Process_Mouse_Click (W: Window); private type Hidden_Window_Type; type Window is access Hidden_Window_Type; end Windows; - - - - - - with Basic_Window; with Alert_Window; package body Windows is type Hidden_Window_Type is record Kind: Integer := 2; end record; type Kind_Of_Window is (Basic, Alert); -- - - - - function To_Basic (W: Hidden_Window_Type) return Basic_Window.Window is ... -- probably an instance of Unchecked_Conversion ---------- procedure Draw (W: Window) is begin case W.all.Kind is when 1 => Basic_Window.Draw (To_Basic (W.all)); ... Now, if you add a new kind of window, you create a new package and update the case statements in Windows. You only compile the new package and the body of Windows. You have almost achieved programming by extension; you have come about as close as possible manually. The Programming-by-Extension facilities in Ada 95 let you eliminate the package Windows. If Basic_Window.Window is a "tagged" type, your event loop's calls to Basic_Window.Draw will be interpreted as being calls to the appropriate Draw for the actual type of Window involved. Another feature of the Programming-by-Extension facilities in Ada 95 is that, when you derive a new type from a tagged type, you can re-use the "parent" type's procedures, if they still apply. For example, suppose that both Basic and Alert windows process mouse clicks the same way; the only difference is how they draw the window. Your code would look like: package Basic_Window is type Window is tagged record ... end record; procedure Draw (W: Window); procedure Process_Mouse_Click (W: Window); end Basic_Window; - - - - - - with Basic_Window; package Alert_Window is -- border flashes red type Window is new Basic_Window.Window with Flashes_Per_Second: Integer; end record; -- Draw is "inherited" -- the Draw from package Basic_Window -- will be used for objects of this type procedure Process_Mouse_Click (W: Window); end Alert_Window; ---- with Basic_Window; procedure Event_Loop is E: Event; -- defined somewhere else function Get_Current_Window return Basic_Window.Window'Class is ... procedure Handle_Event (W: Basic_Window.Window'Class) is begin case E is when Draw_Window => Basic_Window.Draw (W); when Click => Basic_Window.Process_Mouse_Click (W); end case; end Handle_Event; begin loop Get_Event (E); Handle_Event (Get_Current_Window); end loop; end Event_Loop; Note that, in Event_Loop, we don't know how much memory an arbitrary window may need, so we can't just declare an object of type Basic_Window.Window'Class. Instead, we make it a parameter to a helper procedure. Its size may vary at run time, but the compiler and run-time system take care of that "behind the scenes." NOW we have programming by extension available for this system! To add a new kind of window, we create a new type that extends Basic_Window.Window (or Alert_Window.Window). We don't have to recode, or even recompile, ANYTHING -- not the window packages, not event loop, NOTHING! The Ada compiler and run-time take care of calling the right Draw or Process_Mouse_Click routines, based on the actual type of the window returned by Get_Current_Window. I hope you find this extended sketch useful. You can do the same general thing in C++, but you have to make the window type a class, and each new window type a subclass of that class. An instance of the class is an "object." The terms "class" and "object" carry a lot of connotations that may not be true in all cases, for instance that the item in question is a discrete entity that exists in its own right, or that the "classes" involved form a meaningful taxonomy and not just a hierarchy that reflects some implementation concern. I believe there are semantic constraints and extra coding effort if you use C++ for non-object-oriented programming by extension, but I am not familiar enough with the language to give you specific details. (Anyone else?) Best, Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: (long) programming by extension 1999-02-24 0:00 ` (long) programming by extension (was: " Samuel Mize @ 1999-02-24 0:00 ` Samuel Mize 1999-02-25 0:00 ` (shorter and new) " Samuel Mize 0 siblings, 1 reply; 34+ messages in thread From: Samuel Mize @ 1999-02-24 0:00 UTC (permalink / raw) The example I used to describe programming by extension is also used to describe object-oriented programming. So it was a poor choice to show the difference. However, there IS a difference. I'll try again. Suppose you are coding a message handler which will get messages in one format from many interfaces, some of which are not yet known, and perform some complex computation. You can define each interface to have a "get" procedure, and use a tagged type to pick the right "get." package No_Interface is type Use_None is tagged null record; type Interface_Selector is access all Use_Nothing'Class; -- this will raise an exception if called, -- or maybe return a default value procedure Get (Interface: Use_Nothing; Message: Data_Format -- defined elsewhere ); end No_Interface; - - - - - - - - - - - - - - - - - - - - - with No_Interface; package Disk_Interface is type Use_Disk is new No_Interface.Use_None with null record; procedure Get (Interface: Use_Disk; Message: Data_Format); end package; - - - - - - - - - - - - - - - - - - - - - with No_Interface; package Serial_Interface is type Use_Serial is new No_Interface.Use_None with null record; procedure Get (Interface: Use_Serial; Message: Data_Format); end package; - - - - - - - - - - - - - - - - - - - - - with No_Interface; use No_Interface; procedure Message_Handler (Current: Interface_Selector) is D: Data_Format; begin -- some complex computation involving several records for I in whatever_range loop Get (Current.all, D); ... if D.Continued then Get (Current.all, D); ... end loop; end Message_Handler; In no case will the type Use_[whatever] contain any data. You can add interfaces as you need to, and never have to recode -- or even re-compile -- Message_Handler. I would certainly NOT call this object-oriented programming. It's sort of an "infinite case statement." But it does take advantage of the Ada 95 facilities for programming by extension. In a real-world application, something as significant as a major device interface probably WOULD be coded as an "object," if you're using object-oriented technology. This is a contrived example. I HAVE used tagged types to generate such an "infinite case statement," but it was buried in an obscure and proprietary system, so it would be neither clear nor legal for me to reproduce the code. Best, Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: (shorter and new) programming by extension 1999-02-24 0:00 ` (long) programming by extension Samuel Mize @ 1999-02-25 0:00 ` Samuel Mize 1999-02-25 0:00 ` Mike Silva 0 siblings, 1 reply; 34+ messages in thread From: Samuel Mize @ 1999-02-25 0:00 UTC (permalink / raw) I'm too pedantic to leave adequate-enough alone, so I've refined my example of non-object-oriented programming by extension. I integrated that into the text I wrote on that earlier. I hope somebody out there finds this of interest. Sam Mize - - - - - - - - - - Programming by Extension means programming a large system so that new processing cases can be handled by adding new code, without having to recode any existing modules. Language support is extremely helpful for true programming by extension. Object-oriented development is a very specific way of analyzing, designing and coding a system. It provides a way to program by extension -- that's one of its selling points -- but it brings along a lot of other baggage too. Sometimes you want that baggage. That's why "object-oriented" technology exists. But sometimes you just want to do plain functional programming, and be able to extend the system later without having to recode the existing part. Ada supports this also. The Ada 95 Rationale describes how Ada supports programming by extension, but it doesn't really define the term. Here's a concrete example of programming by extension. Consider a function to read up to 20 messages from some interface, compute statistics and generate a summary. The rest of the system only cares about the summary; it doesn't care about the input messages, or about how the interfaces work. One simple approach would put the summarizer function in a separate package, with an enumeration variable to tell it which interface to use: package Simple_Approach is type Interfaces is (Disk_File, Serial_Interface, ...); type Summary_Type is record ... end record; function Summarizer (Interface_To_Use: Interfaces) return Summary_Type; end Simple_Approach; The problem, of course, is that adding a new interface will cause recoding of the type Interfaces and the function Summarizer, and recompilation of anything that "withs" package Simple_Approach. Note that Ada is often used in life-critical systems like avionics, where each unit goes through a long and expensive validation of the compiled object code. This must be redone if the unit is recompiled, even if the source code is not changed. Lets use programming by extension to prevent all this recoding and recompilation. The first question is, what needs to be extended? In this case, the operation of getting a message from an interface. So, we'll create a procedure Get that gets a message from an interface, basing it on a tagged type so it's polymorphic: with Message; package Generic_Interface is type Flag is tagged null record; procedure Get (Which_Interface: Flag; Data: Message.Data_Type); end Generic_Interface; We can now write Summarizer as a class-wide procedure, which uses dispatching to get messages from whatever interface is appropriate: with Generic_Interface; package Extension_Approach is function Summarizer (Interface_To_Use: Generic_Interface.Flag'Class) return Summary_Type; end Extension_Approach; - - - - - - with Messages; package body Extension_Approach is function Summarizer (Interface_To_Use: Generic_Interface.Flag'Class) return Summary_Type is Data: array (1..20) of Message.Data_Type; begin for I in 1 .. 20 loop Get (Interface_To_Use, Data (I)); exit when Data (I).Last_Message; end loop; ... The body of Get in the package Generic_Interface may return an exception, or get messages from a default interface (in which case the package should probably be named Default_Interface). To extend Summarizer to get messages from a new interface, we just extend the type Generic_Interface.Flag, and override its Get: with Message; with Generic_Interface; package Disk_Interface is type Flag is new Generic_Interface.Flag with null record; procedure Get (Which_Interface: Flag; Data: Message.Data_Type); end Disk_Interface; In no case will the type Flag contain any data. Now we can call Summarizer to get messages from disk and return us the summary: Summary := Extension_Approach.Summarizer (Disk_Interface.Flag'(null record)); Even if Disk_Interface was written after Summarizer, we don't need to recompile Summarizer. With a bit more care we can code almost all the user system so that it won't need to be recoded, or even recompiled, to handle any new interface. First we add a declaration to Generic_Interface that lets the user store a flag variable: type Interface_Selector is access constant Flag'Class; end Generic_Interface; In each interface package, we put a constant Selected, so the user won't have to use the rather clunky syntax "TYPENAME'(null record)". We also put an access constant that denotes Selected, which can be stored in a variable of type Interface_Selector. Selected: constant Flag := Flag'(null record); Selection: constant Generic_Interface.Interface_Selector := Selected'Access; end Disk_Interface; (These additions to the _Interface packages are just convenience items, the user code could make these declarations itself.) Now, to hard-code which interface is being used, the code can say: with Disk_Interface; with Extension_Approach; use Extension_Approach; procedure User is Sum: Summary_Type; begin Sum := Summarizer (Disk_Interface.Selected); To encapsulate the knowledge about interfaces in a single package, the code might look like: package Interfaces is Current: Generic_Interface.Interface_Selector; end Interfaces; - - - - - with Interfaces; with Extension_Approach; use Extension_Approach; procedure User is Sum: Summary_Type; begin Sum := Summarizer (Interfaces.Current); ... Now, to extend User, we only have to add another class, and somewhere there will be a bit of code -- possibly buried in the user interface -- that we will have to change so it can set Interfaces.Current to New_Interface.Selection. The entire rest of the system won't have to be recoded, or even recompiled. Another feature of the Programming-by-Extension facilities in Ada 95 is that, when you derive a new type from a tagged type, you can re-use the "parent" type's procedures, if they still apply. For example, suppose that some interfaces must be explicity started and stopped. The code might look like: with Message; package Generic_Interface is type Flag is tagged null record; procedure Get (Which_Interface: Flag; Data: Message.Data_Type); procedure Start (Which_Interface: Flag); -- does nothing procedure Stop (Which_Interface: Flag); -- does nothing ... - - - - - - with Message; package Disk_Interface is type Flag is new Generic_Interface.Flag with null record; procedure Get (Which_Interface: Flag; Data: Message.Data_Type); -- we don't need to start or stop a disk, so the do-nothing Start -- inherited from Generic_Interface is just fine ... - - - - - - with Message; package Serial_Interface is type Flag is new Generic_Interface.Flag with null record; procedure Get (Which_Interface: Flag; Data: Message.Data_Type); -- Start and stop the serial interface procedure Start (Which_Interface: Flag); procedure Stop (Which_Interface: Flag); ... And the user code would look like: with Interfaces; with Extension_Approach; use Extension_Approach; procedure User is Sum: Summary_Type; begin Start (Interfaces.Current); Sum := Summarizer (Interfaces.Current); Stop (Interfaces.Current); ... I hope you find this extended sketch useful. I would certainly NOT call this object-oriented programming. But it does take advantage of the Ada 95 facilities for programming by extension. It's sort of an "infinite case statement." Certainly this is a contrived example. I HAVE used tagged types to generate such an "infinite case statement," but it was buried in an obscure and proprietary system, so it would be neither clear nor legal for me to reproduce the code. You can do the same general things in C++, but you have to make the window type a class, and each new window type a subclass of that class. An instance of the class is an "object." The terms "class" and "object" carry a lot of connotations that may not be true in all cases, for instance that the item in question is a discrete entity that exists in its own right, or that the "classes" involved form a meaningful taxonomy and not just a hierarchy that reflects some implementation concern. Further, I believe there may be extra semantic constraints and extra coding effort in using C++ for non-object-oriented programming by extension. However, I am not familiar enough with the language to give you specific details. Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: (shorter and new) programming by extension 1999-02-25 0:00 ` (shorter and new) " Samuel Mize @ 1999-02-25 0:00 ` Mike Silva 1999-02-26 0:00 ` Samuel Mize 0 siblings, 1 reply; 34+ messages in thread From: Mike Silva @ 1999-02-25 0:00 UTC (permalink / raw) Samuel Mize wrote in message <7b49mm$t7q@news1.newsguy.com>... >I'm too pedantic to leave adequate-enough alone, so I've refined my >example of non-object-oriented programming by extension. I integrated >that into the text I wrote on that earlier. I hope somebody out there >finds this of interest. Thank you very much for your explanations, Sam. I gather (though I haven't read your last message through yet) that programming by extension is essentially the mechanics part of OOP, without the data and methods organization part (which I gather is best expressed in Ada using the package concept). I hope I've got that right, since I feel like I've had a major "oh yeah!" breakthrough. I never thought of separating the two parts before, although seeing it written out like that I recognize that I've done forms of the latter organizing in my non-OO programming. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: (shorter and new) programming by extension 1999-02-25 0:00 ` Mike Silva @ 1999-02-26 0:00 ` Samuel Mize 0 siblings, 0 replies; 34+ messages in thread From: Samuel Mize @ 1999-02-26 0:00 UTC (permalink / raw) Mike Silva <mjsilva@jps.net> wrote: > > Samuel Mize wrote in message <7b49mm$t7q@news1.newsguy.com>... >>I'm too pedantic to leave adequate-enough alone, so I've refined my >>example of non-object-oriented programming by extension. I integrated >>that into the text I wrote on that earlier. I hope somebody out there >>finds this of interest. > > > Thank you very much for your explanations, Sam. I gather (though I haven't > read your last message through yet) that programming by extension is > essentially the mechanics part of OOP, without the data and methods > organization part (which I gather is best expressed in Ada using the package > concept). I hope I've got that right, since I feel like I've had a major > "oh yeah!" breakthrough. Well, your new insight is probably pointed in the right direction, but I can't agree with your phrasing. For an initial insight, though, you're doing very well. > programming by extension is > essentially the mechanics part of OOP, without the data and methods > organization part In a larger sense, it's any technology that lets you extend the capabilities of a system by linking in new code, without having to go in and recode or recompile the existing stuff to integrate it in. The specific examples I've show use Ada 95's polymorphic subprograms to do this. Polymorphism is one mechanism that goes into OOP. But it isn't ALL of the "mechanics part of OOP," and it isn't the only possible approach to "programming by extension." For instance, many Lisp environments take an extremely flexible view of function definition and linkage, and it's normal to build up a program piecemeal. Many of the techniques used in that kind of "rapid prototyping" can be legitimately called "programming by extension." Object-oriented folks like to say that the three touchstones of OO methods are: abstraction, encapsulation, and inheritance. I think it's fair to say that they are talking about abstraction in terms of objects; functional abstractions, or data-flow abstractions, aren't object-oriented. - The non-object-oriented programming by extension that I demonstrated uses a purely functional kind of abstraction. - It doesn't require any encapsulation of data -- all the records may well be publicly visible, and changed arbitrarily by any part of the rest of the system. - While inheritance of functions is used, there is no general inheritance of data structures. So, it fails all three mechanical tests for object-orientation. Furthermore, there is no attempt to create a hierarchy of classes that define the essential abstractions that organize the system. This is the philosophical heart of object-oriented methods. > the mechanics part of OOP, without the data and methods > organization part (which I gather is best expressed in Ada using the package > concept). Well, I'd identify two issues here: - encapsulation (preventing anything but the object's methods from operating on its data) - organization (pulling together all and only the stuff related to the class, for clarity) For any type, not just tagged types, encapsulation is enforced with the package mechanism, but ONLY if you make the type private. So it's really an interaction between typing and packaging. Organization may use packaging, as well as the ordering of the code, naming conventions, and so on. Now, if you want to get a "class" in the style of C++, you will want to define a package that contains only the tagged type with its operations and support items (like constants). But you should be aware that this is only one kind of object-oriented coding. Ada supports a number of other paradigms. Some people will argue violently, even rudely, that that is a very poor kind of object-oriented technology. Because Ada 95 only provides the building blocks, you can do whatever kind of object-oriented development you want. And, you can do other things with those building blocks (like non-object-oriented programming by extension). Best, Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: Question (was Re: How to write TYPECASE in Ada 95?) 1999-02-23 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Mike Silva 1999-02-24 0:00 ` Samuel T. Harris 1999-02-24 0:00 ` (long) programming by extension (was: " Samuel Mize @ 1999-02-24 0:00 ` Nick Roberts 2 siblings, 0 replies; 34+ messages in thread From: Nick Roberts @ 1999-02-24 0:00 UTC (permalink / raw) Object oriented programming is considered by many to be more (or entirely) about a programming methodology (a 'mentality', if you like), rather than just a particular set of programming tools, or the characteristics of a particular programming language. It also has its own argot: you use 'objects' to model real-life entities; you organise those objects into the hierarchies - or 'classes' - their real-world analogues exhibit; objects interact by sending each other 'messages'; and so on. I think Ada 95 was always designed to provide sufficient language 'tools' to support an object-oriented methodology, without actually imposing that (or any other) methodology (or terminology), as such. You can use tagged types simply for 'programming-by-extension', without having to subscribe to the whole object oriented 'mentality'. Or, if you wish, you can use them as a part of a full-blooded object-oriented design approach. As you wish. The thing that has always stood out to me about object oriented designs is that a good design is almost always directly transferable from one programming language to another. There's definitely something fundamental about it. A good OO design can be a great design. Possibly the best thing about it is that it persuades programmers to design at a higher level of abstraction than they otherwise would. It's just a pity that all the ballyhoo surrounding the subject at the moment could easily lead a lot of people to think nothing of it. ------------------------------------- Nick Roberts ------------------------------------- ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 How to write TYPECASE in Ada 95? Norman Ramsey 1999-02-05 0:00 ` Brian Rogoff @ 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` David C. Hoos, Sr. 2 siblings, 0 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-06 0:00 UTC (permalink / raw) nr@labrador.cs.virginia.edu (Norman Ramsey) writes: > I'm trying to figure out from the reference manual how to identify > which type extension of a type I have. For example, if I have > > type Point is tagged > record > X, Y : Real := 0.0; > end record; > > type Painted_Point is new Point with > record > Paint : Color := White; > end record; > > > type Tall_Point is new Point with > record > Height : Real := 0.0; > end record; > > I'd like to be able to discriminate at run time: > > p : Point; > > ... > > if p in Painted_Point then > ... do something with Painted_Point'(p) > else if p in Tall_Point then > ... do something with Tall_Point'(p) You've got it right: use a membership test. The only problem here is that you should have said "elsif" instead of "else if". > Even better would be something like Modula-3 TYPECASE: > > (* modula-3 *) > TYPECASE p OF > Painted_Point (painted) => ... do something with painted > Tall_Point (tail) => ... do something with tall > ... > END No, you can't use case statement for this sort of thing. > What's the idiomatic way to express this in Ada? The idiomatic way in Ada --and in any object-oriented language-- is to not do this at all. You should be call a primitive operation of the type that automatically dispatches according to the (type) tag of the object. If you insist on doing an explicit test, then use a membership test. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-05 0:00 How to write TYPECASE in Ada 95? Norman Ramsey 1999-02-05 0:00 ` Brian Rogoff 1999-02-06 0:00 ` How to write TYPECASE in Ada 95? Matthew Heaney @ 1999-02-06 0:00 ` David C. Hoos, Sr. 1999-02-06 0:00 ` Matthew Heaney 2 siblings, 1 reply; 34+ messages in thread From: David C. Hoos, Sr. @ 1999-02-06 0:00 UTC (permalink / raw) Norman Ramsey wrote in message <79fct8$9k3$1@murdoch.acc.Virginia.EDU>... > >I'm trying to figure out from the reference manual how to identify >which type extension of a type I have. For example, if I have > > type Point is tagged > record > X, Y : Real := 0.0; > end record; > > type Painted_Point is new Point with > record > Paint : Color := White; > end record; > > > type Tall_Point is new Point with > record > Height : Real := 0.0; > end record; > >I'd like to be able to discriminate at run time: > > p : Point; > > ... > > if p in Painted_Point then > ... do something with Painted_Point'(p) > else if p in Tall_Point then > ... do something with Tall_Point'(p) > >Even better would be something like Modula-3 TYPECASE: > > (* modula-3 *) > TYPECASE p OF > Painted_Point (painted) => ... do something with painted > Tall_Point (tail) => ... do something with tall > ... > END > >What's the idiomatic way to express this in Ada? > None of the responses to this posting (including mine) addressed the larger question -- i.e., "What's the idiomatic way to express this in Ada?" Ed Falis' comment made me realize that, so here's an attempt to answer it. First, I would say that it would be presumptuous of me to say something was _the_ idiomatic way to express something in Ada, because with so many of Ada's capabilities there are multiple idioms possible, but not always any one best way to do it. So here is _an_idiomatic way to express this in Ada: This is a complete working program, that I hope is helpful. David C. Hoos, Sr. --------- begin source code ------ package Points is subtype Real is Float; type Color is (Black, Red, Brown, Green, Yellow, Blue, Purple, White); type Point is tagged record X, Y : Real := 0.0; end record; ------------------------------------------------------------------------- ---- -- Show_Type (P: Point'Class); -- Purpose: -- This class-wide procedure illustrates a procedure which serves for all -- types derived from its base type, regardless of future extensions -- unknown or even unanticipated at the time this procedure was written. ------------------------------------------------------------------------- ---- procedure Show_Type (P : Point'Class); ------------------------------------------------------------------------- ---- -- Show_Properties (P: Point); -- Purpose: -- This procedure illustrates a procedure which serves for only the -- type of its parameter. It may be called explicitly with an object -- of its type, or implicitly by means of a dispatching call with a -- class-wide object, in which case the procedure to be called is -- determined at run-time by the actual type of the parameter object. -- NOTE: If other types are derived from Point, a Show_Properties -- procedure MUST be provided for that type. This restriction is -- enforced at compile time. ------------------------------------------------------------------------- ---- procedure Show_Properties (P : Point); type Painted_Point is new Point with record Paint : Color := White; end record; ------------------------------------------------------------------------- ---- -- Show_Properties (P: Painted_Point); -- Purpose: -- See discussion for Show_Properties (P: Point); ------------------------------------------------------------------------- ---- procedure Show_Properties (P: Painted_Point); type Tall_Point is new Point with record Height : Real := 0.0; end record; ------------------------------------------------------------------------- ---- -- Show_Properties (P: Tall_Point); -- Purpose: -- See discussion for Show_Properties (P: Point); ------------------------------------------------------------------------- ---- procedure Show_Properties (P: Tall_Point); end Points; with Ada.Tags; with Ada.Text_Io; package body Points is procedure Show_Properties (P : Point) is begin Ada.Text_Io.Put_Line ("X =>" & Real'Image (P.X) & "Y =>" & Real'Image (P.Y)); end Show_Properties; procedure Show_Properties (P: Painted_Point) is begin Show_Properties (Point (P)); Ada.Text_Io.Put_Line ("Paint =>" & Color'Image (P.paint)); end Show_Properties; procedure Show_Properties (P: Tall_Point) is begin Show_Properties (Point (P)); Ada.Text_Io.Put_Line ("Height =>" & Real'Image (P.Height)); end Show_Properties; procedure Show_Type (P : Point'Class) is begin Ada.Text_Io.Put_Line ("Type => " & Ada.Tags.External_Tag (P'Tag)); end Show_Type; end Points; with Ada.Text_Io; with Ada.Unchecked_Deallocation; with Points; procedure Test_Points is -- Since an object of Points.Point'Class must be constrained at the time of -- declaration to some one of the types derived from Points.Point, and -- therefore cannot be re-used for other types derived from Points.Point, -- we use an access object, instead. But first, we declare the type of -- that access object, as well as a deallocation procedure for it. type Point_Class_Access is access all Points.Point'Class; procedure Free is new Ada.Unchecked_Deallocation (Object => Points.Point'Class, Name => Point_Class_Access); -- We do not need to initialize this access object here, but we do so -- just to illustrate that it can be done. P_Access : Point_Class_Access := new Points.Tall_Point'(3.0, 4.0, 5.0); begin -- Note that this is _not_ a dispatching call, because the procedure's -- formal parameter is of a class-wide type. Points.Show_Type (P_Access.all); -- Note that this _is_ a dispatching call, because the object is of a -- class-wide type. Points.Show_Properties (P_Access.all); -- P_Access.Paint := Blue; Illegal P_Access accesses a Tall_Point object, -- so we must first deallocate the memory for the object, leaving it free -- to access another object of a type derived from Points.Point. Free (P_Access); P_Access := new Points.Painted_Point'(3.0, 4.0, Points.Blue); Points.Show_Type (P_Access.all); Points.Show_Properties (P_Access.all); end Test_Points; --------- end source code ------ ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` David C. Hoos, Sr. @ 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney ` (2 more replies) 0 siblings, 3 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-06 0:00 UTC (permalink / raw) "David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> writes: > procedure Test_Points is > > -- Since an object of Points.Point'Class must be constrained at the time > of > -- declaration to some one of the types derived from Points.Point, and > -- therefore cannot be re-used for other types derived from Points.Point, > -- we use an access object, instead. But first, we declare the type of > -- that access object, as well as a deallocation procedure for it. > type Point_Class_Access is access all Points.Point'Class; > > procedure Free is new Ada.Unchecked_Deallocation > (Object => Points.Point'Class, > Name => Point_Class_Access); > > -- We do not need to initialize this access object here, but we do so > -- just to illustrate that it can be done. > P_Access : Point_Class_Access := new Points.Tall_Point'(3.0, 4.0, 5.0); Note that there's another way to illustrate these concepts, that does not require heap. Just use a declare block to declare each new kind of point, as follows: with Ada.Text_Io; with Ada.Unchecked_Deallocation; with Points; use Points; procedure Test_Points is begin declare P : Tall_Point := (3.0, 4.0, 5.0); begin Points.Show_Type (P); Points.Show_Properties (Point'Class (P)); end; declare P : Painted_Point := (3.0, 4.0, Points.Blue); begin Points.Show_Type (P); Points.Show_Properties (Point'Class (P)); end; end Test_Points; Declare blocks are your friend. Unnecessary use of heap is your enemy. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Matthew Heaney @ 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney 1999-02-09 0:00 ` David C. Hoos, Sr. 2 siblings, 0 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-06 0:00 UTC (permalink / raw) Matthew Heaney <matthew_heaney@acm.org> writes: > with Points; use Points; > procedure Test_Points is > begin > > declare > P : Tall_Point := (3.0, 4.0, 5.0); > begin > Points.Show_Type (P); > Points.Show_Properties (Point'Class (P)); > end; > > declare > P : Painted_Point := (3.0, 4.0, Points.Blue); > begin > Points.Show_Type (P); > Points.Show_Properties (Point'Class (P)); > end; > > end Test_Points; I should have pointed out that you don't actually need to dispatch Show_Properties. At the invocation, you know the specific type of the point, so why convert it to the class-wide type? procedure Test_Points is begin declare P : Tall_Point := (3.0, 4.0, 5.0); begin Points.Show_Type (P); Points.Show_Properties (P); end; declare P : Painted_Point := (3.0, 4.0, Points.Blue); begin Points.Show_Type (P); Points.Show_Properties (P); end; end Test_Points; The object model of Ada95 was designed to allow the caller to choose whether dispatching occurs. All the calls in the example above are static, which is all they need to be. ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney @ 1999-02-06 0:00 ` Matthew Heaney 1999-02-09 0:00 ` David C. Hoos, Sr. 2 siblings, 0 replies; 34+ messages in thread From: Matthew Heaney @ 1999-02-06 0:00 UTC (permalink / raw) Matthew Heaney <matthew_heaney@acm.org> writes: > Note that there's another way to illustrate these concepts, that does > not require heap. Just use a declare block to declare each new kind of > point, as follows: Here is yet another way to do this, by declaring the objects to be type Point'Class. This is probably closer to David's original example. Like my earlier post, the example here doesn't require the use of heap. with Ada.Text_Io; with Ada.Unchecked_Deallocation; with Points; use Points; procedure Test_Points is begin declare P : Point'Class := Tall_Point'(3.0, 4.0, 5.0); begin Points.Show_Type (P); Points.Show_Properties (P); end; declare P : Point'Class := Painted_Point'(3.0, 4.0, Points.Blue); begin Points.Show_Type (P); Points.Show_Properties (P); end; end Test_Points; ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: How to write TYPECASE in Ada 95? 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney @ 1999-02-09 0:00 ` David C. Hoos, Sr. 2 siblings, 0 replies; 34+ messages in thread From: David C. Hoos, Sr. @ 1999-02-09 0:00 UTC (permalink / raw) Matthew Heaney wrote in message ... >"David C. Hoos, Sr." <david.c.hoos.sr@ada95.com> writes: <large-snip> > >Declare blocks are your friend. Unnecessary use of heap is your enemy. Agreed. In fact, often the appropriate thing to do is use a declare block -- e.g., when reading from a stream, and the type is unknown -- but in this case I was also attempting to show that the same _object_ could refer to any descendnts of the root class. ^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~1999-02-26 0:00 UTC | newest] Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 1999-02-05 0:00 How to write TYPECASE in Ada 95? Norman Ramsey 1999-02-05 0:00 ` Brian Rogoff 1999-02-05 0:00 ` David C. Hoos, Sr. 1999-02-05 0:00 ` Brian Rogoff 1999-02-06 0:00 ` Ed Falis 1999-02-06 0:00 ` Nick Roberts 1999-02-06 0:00 ` Nick Roberts 1999-02-17 0:00 ` Tom Moran 1999-02-18 0:00 ` Matthew Heaney 1999-02-18 0:00 ` robert_dewar 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Nick Roberts 1999-02-18 0:00 ` Tom Moran 1999-02-18 0:00 ` Tom Moran 1999-02-18 0:00 ` Matthew Heaney 1999-02-19 0:00 ` Tom Moran 1999-02-19 0:00 ` Tom Moran 1999-02-23 0:00 ` Samuel Mize 1999-02-23 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Mike Silva 1999-02-24 0:00 ` Samuel T. Harris 1999-02-24 0:00 ` Matthew Heaney 1999-02-24 0:00 ` Tucker Taft 1999-02-24 0:00 ` (long) programming by extension (was: " Samuel Mize 1999-02-24 0:00 ` (long) programming by extension Samuel Mize 1999-02-25 0:00 ` (shorter and new) " Samuel Mize 1999-02-25 0:00 ` Mike Silva 1999-02-26 0:00 ` Samuel Mize 1999-02-24 0:00 ` Question (was Re: How to write TYPECASE in Ada 95?) Nick Roberts 1999-02-06 0:00 ` How to write TYPECASE in Ada 95? Matthew Heaney 1999-02-06 0:00 ` David C. Hoos, Sr. 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney 1999-02-06 0:00 ` Matthew Heaney 1999-02-09 0:00 ` David C. Hoos, Sr.
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox