From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,5ea4e8c5a1c5a499 X-Google-Attributes: gid103376,public From: smize@news.imagin.net (Samuel Mize) Subject: Re: Polymorphism question Date: 1997/02/20 Message-ID: <5ejaqs$d7c@prime.imagin.net> X-Deja-AN: 220336144 References: <330C9CB8.41C6@pheno.physics.wisc.edu> Organization: ImagiNet Communications Ltd, Arlington, Texas Newsgroups: comp.lang.ada Date: 1997-02-20T00:00:00+00:00 List-Id: In article <330C9CB8.41C6@pheno.physics.wisc.edu>, Robert Oeckl wrote: >I have problems to figure out, how Polymorphism works in Ada. I want to >know if and how the following is possible in Ada: Yes, it is. Here's how polymorphism works in Ada. First, you learn the "polymorph self" spell -- wait, that's Dungeons & Dragons. NOTE: by convention, "Ada" is Ada 95. This capability is not in Ada 83. NOTE ALSO: I don't have a compiler or ARM handy. Consider the code snippets to be suggestive, not authoritative. In Ada, most data is unimorphic. If you want a type to be polymorphic, you must explicitly say so. Such a type is a "tagged" type. You cannot derive a tagged type from a non-tagged type. It must be a record type (since you are planning to add fields to it). The "tag" is an implicit record field that identifies, for a specific object, which exact type it is. A "class" is a tree of related types: a specific tagged type and all the tagged types derived from it. Any type has "primitive" subprograms.[1] A tagged type's primitive subprograms may "dispatch" -- execute the appropriate procedure based on the specific type of the controlling parameter or result.[2] However, this only happens if the type of the controlling parameter (or result) is not statically defined. Thus, in the following, P1, P2 and P3 can dispatch, but all the calls actually shown are statically determined. (I'll show dispatching next.) package T1_Class is type T1 is tagged record X: float; end record; procedure P1 (T: T1); procedure P2 (T: T1); procedure P3 (T: T1); end T1_Class; package T2_Class is type T2 is new T1 with record I: integer; -- T2 has BOTH field X and field I end record; procedure P1 (T: T2); procedure P2 (T: T1); end T2_Class; procedure Example1 is X1: T1; X2: T2; begin P1 (X1); -- calls T1_Class.P1 -- A P3 (X1); -- calls T1_Class.P3 -- B P1 (X2); -- calls T2_Class.P1 -- C P3 (X2); -- calls T1_Class.P3 -- D end Example1; You can view-convert a given tagged data item to any of its "parent" types. Thus, you can convert X2 up to type T1, but you CANNOT convert X1 down to type T2 -- what would you use for the value of field I? The type conversion looks like: P1 ( T1(X2) ); -- calls T1_Class.P1 You can also tell Ada to dispatch, even though you have an object of a specific type, by type-converting it to the class of the object's type (or a parent type): P2 ( T1'class (X2) ); -- DISPATCHES to the P2 defined for -- T1's actual type (based on its tag) THE TAG TRAVELS WITH THE DATA ITEM and remains the same. (It's just another field of the record [3]). Now, suppose that in T1_Class.P1 we make the call: P2 ( T1'class (X2) ); -- E When Example1 calls P1 at C, it will call T1_Class.P1; but when T1_Class.P1 calls P2 at E, it will DISPATCH to call T2_Class.P2, even though T2_Class did not exist when T1_Class's body was compiled! The program doesn't lose the knowledge of what type the object really is. Finally, a data item can be of a class-wide type (WARNING: I don't have an ARM handy. I KNOW parameters can be class-wide, I THINK variables can be. I KNOW accesses-to-variables can be.) A call to a dispatching operation with a class-wide data item will, of course, dispatch. procedure Example2 (P: T1'class) is type T_Class_Access is access T1'class; X1: aliased T1'class := P; X2: T_Class_Access := X1'Unchecked_Access; begin P1 (P); -- DISPATCHES based on tag of actual parameter P1 (X1); -- DISPATCHES P1 (X2.all); -- DISPATCHES end Example2; I hope you find this a helpful introduction. I understand there is a paper discussing object-oriented programming in Ada somewhere under http://www.adahome.com . Certainly there you can get the Ada 95 Rationale, which has a section on this very topic. Samuel Mize FOOTNOTES: [1] Exactly which subprograms are "primitive" is based on the "freezing" rules in Ada. I don't know them off the top of my head. The usual idiom is to put each type and its subprograms in a distinct package; these will all be primitive, and will be all the type's primitives. This does not have to be a library-level package: in the following, P1 is a primitive subprogram for T1, but P2 is not. package Enclosure is package T1_Class is type T1 is ...; procedure P1 (T: T1); end T1_Class; procedure P2 (T: T1); end Enclosure; [2] A subprogram may have more than one controlling parameter/result, but you can't dispatch to two procedure bodies. All the controlling parameters, and the result (if controlling), must be of the same specific type. The rules are a little more complex than that, but that's the basic idea. [3] Not necessarily -- there are possible implementations of the Ada functionality that don't use an actual record field as the tag. If you aren't building an Ada 95 compiler, don't worry about it.