Talk:Ada Programming/Object Orientation

== Be careful when changing characters

This remark goes to the changes made on 31 July 2017‎ by PokestarFan:

This "—" is not a legal character in Ada code outside of comments. Ada comments are started with "--".

Do not without good reason rearrange comments by shifting or combining lines.

Note on Terminology
A request.

It is perhaps tricky business talking about Ada O-O, C++ O-O, and informal O-O at the same time and not make things misleadingly imprecise. E.g., C++ has derived classes and base classes, but no subtypes; in particular, no Ada subtypes (that have a constrained subset of values), nor Ada base types. Discussing Ada O-O using "base type" for parent type or progenitor type thus confuses issues. One reason is that "base type" has been a defined term in Ada ever since, albeit meaning something different. There even is a  attribute, "denoting an unconstrained subtype of the type of S", LRM 3.5(15). You see? Also, this informal vocabulary may not work so well with multiple inheritance as defined in the respective language—to be used, after all—even if from the academic vernacular. Say, using the words “cast”, and “upcast” other than by contrasting with informal speech is almost certain to either mislead or be factually inadequate, thus misguiding programmers even if this style is conveniently used by some projects. But this is a book.

So, in this sense (also, as an apology for silently reverting a comment written using "red ink"), e.g., what exactly is an "inherited method"? When an implicitly inherited Ada subprogram, not overridden, but a user-defined primitive operation of the parent O-O type, it has nothing to to with an Ada base type. And it may be factually wrong to imply what it is by imposing some other language's rules, although what is the case might be contrasted with how a base class works in C++. The Ada parameter profile here does use the derived type['s subtype] by substitution (LRM 3.5(18)), even when there may be (view) conversions involved when calling.

So, I say that this page needs to have very clear definitions, some kind of implicit or explicit qualification of terms used, always, and no analogies, in order to be guiding properly. There are wikibook templates for referring to other places or the LRM. gb (discuss • contribs) 11:29, 22 April 2017 (UTC)

C++ vs. Ada: Class-wide parameters
Clarification to the revision 8 March 2012. The changes have been undone because they are in error.

package Pack is  type S is tagged ...; procedure P (X: S);       -- 1 procedure Q (X: S'Class); -- 2 type T is new S with ...; -- procedure P (X: T); -- 3 is implicitely defined, i.e. derived from 1. It may be overridden. -- Q is not derived, it's not a primitive operation. It cannot be overridden. end Pack;

Now assume there are declarations like these:

Specific_Object: Pack.T; ... Classwide_Object: Pack.S'Class := Specific_Object; -- must be initialised, may be of any type within the derivation hierarchy of S

Then you may call

Pack.P (Specific_Object);  -- calls P 3 or its overriding, static binding, no dispatching Pack.Q (Specific_Object);  -- calls Q 2, static binding, no dispatching (*) Pack.P (Classwide_Object); -- dispatches to the implicitly declared P 3 or its overriding Pack.Q (Classwide_Object); -- calls Q 2, static binding, no dispatching (**)

Now assume the body of Q holds a call like

procedure Q (X: S'Class) is begin P (X); end Q;

Then for either * or **, the call within Q to P dispatches to P 3.

Hope this helps.

C++ Conversion Attempt to Ada
The following (as of 2010-07-19) is a copy of code under the headline Encapsulation: public, private and protected members.

Quote:

In C++, the unit of encapsulation is the class; in Ada, the unit of encapsulation is the package. This has consequences on how an Ada programmer places the various components of an object type.

class C { public: int a;  void public_proc; protected: int b;  int protected_func; private: bool c;  void private_proc; };

In Ada it is necessary to split C into three types, one holding each of the public, protected and private parts:

package P is  type Public_Part is tagged record A: Integer; end record; function Construct return Public_Part'Class; procedure Public_Proc (This: in out Public_Part); private -- this part is visible only in child packages and in the body type Protected_Part is new Public.Public_Part with record B: Integer; end record; function Protected_Func (This: Protected_Part) return Integer; end P; package body P is  type Private_Part is new Protected_Part with record C: Boolean; end record; procedure Private_Proc (This: in out Private_Part); ...  function Construct return Public_Part'Class is   begin return Result: Private_Part do      ...; end return; end Construct; end P;

End Quote.

In my opinion, this Ada code is complete nonsense. You can declare a C++ object of class C with components a, b, c. You cannot declare an Ada object of Private_Part, only class-wide objects. But you cannot be sure that such a class-wide object has components B or C.

This looks like a contorted attempt to mimic C++ which completely fails. A better translation is perhaps the following (just a sketch):

package CPP is  type Public_Part is tagged record A: Integer; end record; procedure Public_Proc (This: in out Public_Part); type Complete_Type is new Public_Part with private; -- procedure Public_Proc (This: in out Complete_Type); -- inherited, implicitly defined private type Private_Part; type Private_Part_Pointer is access Private_Part; type Complete_Type is new Public_Part with record B: Integer; P: Private_Part_Pointer; -- should be controlled to avoid storage leaks end record; not overriding function Protected_Func (This: Complete_Type) return Integer; end CPP; package body CPP is  procedure Public_Proc (This: in out Public_Part) is begin ... end Public_Proc; type Private_Part is ...; not overriding function Protected_Func (This: Complete_Type) return Integer is begin ... end Protected_Func; end CPP;


 * I know nothing about Ada. However, I don't think readers would like to see the page printed with 'this is incorrect', so I hid it (I didn't remove it; you can still see it in edit mode). I hope you understand that any minute, someone could be ordering a copy of the book, and they might be unhappy to see that there's a note on there saying 'this is wrong'. You're better off replacing the wrong stuff with the right stuff, or removing the whole wrong thing once and for all. Kayau ( talk &#124; email &#124; contribs ) 10:05, 2 August 2010 (UTC)

Objects of class-wide types
The section *The class-wide-type* claims that “in fact, there are no objects of this class-wide type - an object always is of a specific child type”. Is this correct, formally? The RM says, in Note 29:


 * “Class-wide types are defined to have unknown discriminants …. This means that objects of a class-wide type have to be explicitly initialized …” [my emphasis]

So, Ada seems to define objects of a class-wide type? I would guess that even though these objects are of a specific type, they are of a class-wide type, too. — gb 18:46, 7 August 2007 (UTC)

I think that it's a matter of the definition of the term object. The glossary definition reads:


 * “An object is either a constant or a variable. An object contains a value.” ( annotation 2.a)

My interpretation is that in the situation at hand, the object is the named item (constant or variable) which is of class-wide type, and which always contains a value of a specific type. The confusion is because in that sentence the term object is being used in the OO sense, as the value contained in the Ada object. For further confusion, consider that a formal parameter can provide a view of an object containing a value, and that view can specify yet another type. DougP (talk) 21:35, 29 August 2008 (UTC)

Structure?
Note that objects' structure is not a good guide to understanding Ada's O-O style. Traditionally, the word "structure" may invoke certain notions of type compatibility which could easily confuse matters here: One can have untagged types that use different representations yet are convertible, e.g. differing in ; one can also have a tagged type derived from another with only a null extension added. So, in the first case, arguably, the “structure” is different, whereas in the second case, the “structure” does not change. If components are what is meant by structure, the argument about untagged types appears differently, but, still in view of null extensions, references to structure seem bound to be confusing whenever deriving a type does not actually add components.

With more emphasis, Ada types are the same if any only if they resolve to the same name. So, even when both  and are derived immediately from, if they (textually) add the same extension then they may have the exact same structure but still differ in name and, therefore, are different tagged types and are not convertible. gb (discuss • contribs) 19:34, 21 May 2017 (UTC)

How to deal with terminology?
The chasm between Ada's use of various words and other languages' use of those words is a thorny problem.

It seems to me that a book on programming Ada must use Ada's terminology. That makes the discussion difficult to follow for programmers familiar with other languages.

One approach is adding translations in situ. I'm not sure that providing each translation only once is enough, but providing a translation at every usage seriously clutters up the narrative. It also makes for more work for editors.

On the other hand, a separate glossary makes for difficult slogging for the person who is unfamiliar with Ada's terminology. Continuously switching between text and glossary is very disruptive to the reading and comprehension.

Is there a book-wide recommendation somewhere? Or does anyone have any suggestions? DougP (talk) 13:11, 30 August 2008 (UTC)

Rewrite needed?
I have a number of problems with the current page:
 * It appears to have been written for Ada 95 with a few additions for some of Ada 2005
 * It doesn't seem to address "OO for the Ada practitioner" at all
 * The "Ada for the OO practitioner" narrative is (probably unavoidably) incomplete and is aimed mainly (entirely?) at C++ programmers without consideration for the other OO languages
 * There are a number of minor factual errors
 * Terminology is inconsistent (see my note above)
 * The distinction between specific types and class-wide types is not made...
 * Which leads to not discussing class-wide operations vs. primitive operations...
 * Which in turn leads to not mentioning at all the significant issue of nested dynamic calls.

I'm considering making the attempt to rewrite this page, but it is a daunting task. It's also difficult to accomplish a full rewrite in a bunch of small update sessions. DougP (talk) 13:37, 30 August 2008 (UTC)

Demonstrating public/protected/private without dynamic memory allocation
The section Encapsulation: public, private and protected members demonstrates a technique for approximating C++'s public/protected/private via the use of a child package for "protected" and a record defined in the package body for "private," where the private extension is allocated dynamically and referred to by an access type in the "protected" record. It appears that this section did not originally use dynamic allocation, but was modified at the suggestion of this discussion, which includes the original example. Of course, the code from the discussion's suggestion was subsequently modified to use Controlled to manage the "private" record.

It's not necessary to use dynamic memory allocation to achieve the goals of the example in this section, and I suggest it be updated to show a better technique. I have created a possible alternative that is designed to achieve the same public/protected/private semantics as the current example. Here's a portion of the spec from my alternative:

package CPP is   -- -- Public API --   subtype Component is String (1..2); type Public_Part is abstract tagged record A : Component := " A"; end record; procedure Public_Proc (This : Public_Part;                          Indent : String) is abstract; --   -- Public Parent_Type Declaration --   type Parent_Type is new Public_Part with private; private --   -- Protected API --   type Protected_Part is abstract new Public_Part with record B : Component := " B"; end record; procedure Protected_Proc (This : Protected_Part;                             Indent : String) is abstract; --   -- Implementation of Parent_Type --   package Parent_Impl is       type Private_Part is new Protected_Part with private; overriding procedure Public_Proc (This : Private_Part;                                        Indent : String); overriding procedure Protected_Proc (This : Private_Part;                                           Indent : String); private type Private_Part is new Protected_Part with record C : Component := " C"; end record; procedure Private_Proc (This: Private_Part;                              Indent : String); end Parent_Impl; --   -- Protected Parent_Type Declaration --   type Parent_Type is new Parent_Impl.Private_Part with null record; end CPP;

I believe this accomplishes the same semantics without all the complexities/overhead of hitting the pool/heap. I think it also demonstrates two techniques that I found very useful in transitioning from C++ to Ada: 1) the private full declaration of a type can extend a different type than the partial public declaration if the private base derives the public base and 2) nested packages can be used to hide data from child packages.

Of course, I think all the examples so far -- including mine -- don't really achieve C++'s "protected," and are more similar to Java's "package private."

If anyone is interested, I am including my full example and its output. I've only been using Ada for a couple weeks, so my code would most certainly need the review of an expert.

SeanD (discuss • contribs) 21:59, 8 January 2013 (UTC)

Full Example
test_cpp.adb with Ada.Text_IO; use Ada.Text_IO; with CPP.Child; use CPP, CPP.Child; procedure Test_CPP is   As_Child : Child_Type; As_Parent : Parent_Type renames Parent_Type (As_Child); As_Parent_Class : Parent_Type'Class := As_Parent; begin Put_Line ("Test_CPP invoking As_Parent.Public_Proc"); As_Parent.Public_Proc (CPP.Indent_Inc); Put_Line ("Test_CPP invoking As_Child.Public_Proc"); As_Child.Public_Proc (CPP.Indent_Inc); Put_Line ("Test_CPP invoking As_Parent_Class.Public_Proc"); As_Parent_Class.Public_Proc (CPP.Indent_Inc); Put_Line ("Test_Cpp invoking As_Child.Dispatch_Protected"); As_Child.Dispatch_Protected (CPP.Indent_Inc); end Test_CPP;

cpp.ads package CPP is   Indent_Inc : constant String := "|  "; --   -- Public API --   subtype Component is String (1..2); type Public_Part is abstract tagged record A : Component := " A"; end record; procedure Public_Proc (This : Public_Part;                          Indent : String) is abstract; procedure Dispatch_Protected (This : Public_Part;                                 Indent : String) is abstract; --   -- Public Parent_Type Declaration --   type Parent_Type is new Public_Part with private; private --   -- Protected API --   type Protected_Part is abstract new Public_Part with record B : Component := " B"; end record; procedure Protected_Proc (This : Protected_Part;                             Indent : String) is abstract; --   -- Implementation of Parent_Type --   package Parent_Impl is       type Private_Part is new Protected_Part with private; overriding procedure Public_Proc (This : Private_Part;                                        Indent : String); overriding procedure Dispatch_Protected (This : Private_Part;                                               Indent : String); overriding procedure Protected_Proc (This : Private_Part;                                           Indent : String); private type Private_Part is new Protected_Part with record C : Component := " C"; end record; procedure Private_Proc (This: Private_Part;                              Indent : String); end Parent_Impl; --   -- Protected Parent_Type Declaration --   type Parent_Type is new Parent_Impl.Private_Part with null record; end CPP; cpp.adb with Ada.Text_IO; use Ada.Text_IO; package body CPP is   package body Parent_Impl is       procedure Public_Proc (This : Private_Part;                              Indent : String) is       begin Put_Line (Indent & "Private_Part.Public_Proc" & This.A & This.B & This.C); This.Protected_Proc (Indent & Indent_Inc); This.Private_Proc (Indent & Indent_Inc); end Public_Proc; overriding procedure Dispatch_Protected (This : Private_Part;                                               Indent : String) is       begin Put_Line (Indent & "Private_Part.Dispatch_Protected" & This.A & This.B & This.C); Protected_Part'Class (This).Protected_Proc (Indent & Indent_Inc); end Dispatch_Protected; overriding procedure Protected_Proc (This : Private_Part;                                           Indent : String) is       begin Put_Line (Indent & "Private_Part.Protected_Proc" & This.A & This.B & This.C); Private_Proc (This, Indent & Indent_Inc); end Protected_Proc; procedure Private_Proc (This : Private_Part;                              Indent : String) is       begin Put_Line (Indent & "Private_Part.Private_Proc" & This.A & This.B & This.C); end Private_Proc; end Parent_Impl; end CPP; cpp-child.ads package CPP.Child is   type Child_Type is new Parent_Type with null record; overriding procedure Public_Proc (This : Child_Type;                                     Indent : String); private overriding procedure Protected_Proc (This : Child_Type;                                        Indent : String); end CPP.Child; cpp-child.adb with Ada.Text_IO; use Ada.Text_IO; package body CPP.Child is   overriding procedure Public_Proc (This : Child_Type;                                      Indent : String) is    begin Put_Line (Indent & "Child_Type.Public_Proc" & This.A & This.B); Parent_Type (This).Public_Proc (Indent & Indent_Inc); Parent_Type (This).Protected_Proc (Indent & Indent_Inc); This.Protected_Proc (Indent & Indent_Inc); end Public_Proc; overriding procedure Protected_Proc (This : Child_Type;                                        Indent : String) is    begin Put_Line (Indent & "Child_Type.Protected_Proc" & This.A & This.B); Parent_Type (This).Public_Proc (Indent & Indent_Inc); Parent_Type (This).Protected_Proc (Indent & Indent_Inc); end Protected_Proc; end CPP.Child;

Output
Test_CPP invoking As_Parent.Public_Proc | Private_Part.Public_Proc A B C |  |  Private_Part.Protected_Proc A B C |  |  |  Private_Part.Private_Proc A B C |  |  Private_Part.Private_Proc A B C Test_CPP invoking As_Child.Public_Proc | Child_Type.Public_Proc A B |  |  Private_Part.Public_Proc A B C |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C |  |  |  Private_Part.Private_Proc A B C |  |  Private_Part.Protected_Proc A B C |  |  |  Private_Part.Private_Proc A B C |  |  Child_Type.Protected_Proc A B |  |  |  Private_Part.Public_Proc A B C |  |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  |  Private_Part.Private_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C Test_CPP invoking As_Parent_Class.Public_Proc | Child_Type.Public_Proc A B |  |  Private_Part.Public_Proc A B C |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C |  |  |  Private_Part.Private_Proc A B C |  |  Private_Part.Protected_Proc A B C |  |  |  Private_Part.Private_Proc A B C |  |  Child_Type.Protected_Proc A B |  |  |  Private_Part.Public_Proc A B C |  |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  |  Private_Part.Private_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C Test_Cpp invoking As_Child.Dispatch_Protected | Private_Part.Dispatch_Protected A B C |  |  Child_Type.Protected_Proc A B |  |  |  Private_Part.Public_Proc A B C |  |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  |  Private_Part.Private_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C |  |  |  Private_Part.Protected_Proc A B C |  |  |  |  Private_Part.Private_Proc A B C

Need to describe how to assigning to access to classwide type.
The description of polymorphism is inadequate in the place where this line occurs:

function Read_From_Disk return Array_Of_Persons is separate;

because it does not show how to assign to the access variables that comprise the elements of the array.

97.117.195.126 (discuss) 08:10, 2 May 2013 (UTC)