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,56dbd715fa74735a X-Google-Attributes: gid103376,public From: John Volan Subject: Re: Mutually dependent private types Date: 1998/05/29 Message-ID: <356F2910.E3BD7C4F@ac3i.dseg.ti.com> X-Deja-AN: 357764546 Content-Transfer-Encoding: 7bit References: <6k25ra$6j7$1@nnrp1.dejanews.com> <3565B105.9BFB4788@ac3i.dseg.ti.com> <356B226F.EF05E927@ac3i.dseg.ti.com> <356C8A02.44354B09@ac3i.dseg.ti.com> <356E09A1.B493FE89@ac3i.dseg.ti.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Organization: Raytheon Systems Company, Advanced C3I Systems Newsgroups: comp.lang.ada Date: 1998-05-29T00:00:00+00:00 List-Id: Matthew Heaney wrote: > > The pragma idea is more or less equivalent to "with P.T". > > Thinking about it more, you could name it thusly: > > with P; > pragma Elaborate_None (P); > package Q is ...; > > So the answer to your question is that the spec for P is not elaborated > prior to its use (with certain constraints) by Q. Hmm... I'm not sure a pragma per se is the ultimate solution, but there's something interesting implied by your suggestion: Let the compiler do the work. Here's what I mean: So far, all the proposals for language extensions have placed the burden on the programmer to decide when and how to resolve a circular spec dependency. Tucker's "with type" clause, my "package abstracts", and Norman Cohen's "package parts" all require the programmer to decide that forward declarations are needed, and then to write those forward declarations in a separate place apart from the corresponding full declarations. (Yes, even Tucker's "with type" forces you into that, in a way. See footnote (*).) That seemed to be in keeping with Ada's language design principles, because Ada already forces you to write interfaces and implementations in separate places (specs and bodies). However, I can understand how having to pull out forward declarations can be something of an annoyance. (I'm sure the typical Eiffel or Java programmer would find it annoying to have to pull specs and bodies apart, too... :-) But another common element in these proposals is that they all basically boil down to finding a way to weaken the impact of the conventional with-clause. Well, why not just introduce a relaxed form of "with", and leave it at that? Such a clause should provide pretty much the same level of visibility to the contents of a package spec as the conventional with-clause does, but only in a "forward" manner that does not imply an elaboration-order dependency. Make it the compiler's job to determine what you can legally use out of the package spec when you import it with such a relaxed with-clause; don't require the author of that spec to pull the forward declarations out. Some care would need to be taken to come up with a good set of rules to put into the RM; they'd probably be comparable in complexity to the "freezing" rules. Compiler-writers would have to be careful to properly enforce those rules, but that's what compilers are for. One element that I think would need to be retained from the "with type" and "package abstract" proposals: If you use this relaxed form of with-clause to get "forward" visibility to a package spec, you should be forced to use the conventional with-clause to get "full" visibility, before being allowed to do things that would require elaboration of the package (for example, creating an instance of a type or invoking a subprogram). For that reason, I'm not sure a pragma is enough, I think you need some way to syntactically distinguish the relaxed vs. strong forms of "with". How about this: Adopt my "with abstract" clause, but without the "package abstract" construct. Just leave the package abstract as something implied, something the compiler could derive for itself from the package spec. (If you don't like my recycling of the "abstract" keyword, come up with some other syntax.) For example: with abstract Patients; -- does not force elaboration with Sets; package Doctors is type Doctor_Type is tagged limited private; type Doctor_Access_Type is access all Doctor_Type'Class; package Doctor_Sets is new Sets (Element_Type => Doctor_Access_Type); procedure Treat_Patient (Doctor : in out Doctor_Type; Patient : in out Patients.Patient_Type'Class); -- okay "forward" usage of Patient_Type, -- because type is pass-by-reference procedure Bill_Patient (Doctor : in out Doctor_Type; Patient : in out Patients.Patient_Type'Class); procedure Add_Regular_Patient (Doctor : in out Doctor_Type; Patient : in Patients.Patient_Access_Type); -- okay "forward" usage of Patient_Access_Type, -- even though pass-by-value, because type-size is -- statically determined without need for elaboration function Get_Regular_Patients (Doctor : in Doctor_Type) return Patients.Patient_Sets.Set_Type; -- Here's where things get dicey: Would this -- be an okay "forward" usage of a generic -- instantiation? ... end Doctors; with abstract Doctors; -- does not force elaboration with Sets; package Patients is type Patient_Type is tagged limited private; type Patient_Access_Type is access all Patient_Type'Class; package Patient_Sets is new Sets (Element_Type => Patient_Access_Type); procedure Visit_Doctor (Patient : in out Patient_Type; Doctor : in out Doctors.Doctor_Type'Class); -- okay "forward" usage of Doctor_Type, -- because type is pass-by-reference procedure Pay_Doctor (Patient : in out Patient_Type; Doctor : in out Doctors.Doctor_Type'Class); procedure Set_Family_Doctor (Patient : in out Patient_Type; Doctor : in Doctors.Doctor_Access_Type); -- okay "forward" usage of Doctor_Access_Type, -- even though pass-by-value, because type-size is -- statically determined without need for elaboration function Get_Family_Doctor (Patient : in Patient_Type) return Doctors.Doctor_Access_Type; ... end Patients; with Patients; -- forces elaboration package body Doctors is procedure Bill_Patient (Doctor : in out Doctor_Type; Patient : in out Patients.Patient_Type'Class) is begin ... Patients.Pay_Doctor (Patient, Doctor); -- requires fully-elaborated access to Patients end Bill_Patient; ... end Doctors; with Doctors; -- forces elaboration package body Patients is procedure Visit_Doctor (Patient : in out Patient_Type; Doctor : in out Doctors.Doctor_Type'Class) is begin ... Doctors.Treat_Patient (Doctor, Patient); -- requires fully-elaborated access to Doctors end Visit_Doctor; ... end Patients; One problem with this: It only works at the library level. What if the two packages involved are nested inside another package? There doesn't seem to be anything analogous you could do. In my FAQ, I felt it was important that any solution that could work with library-level packages should also work with nested packages. I demonstrated how my package abstracts could work even in a nested context, and I also showed that Tucker's proposal could also work, if you generalize his "with type" idea to include something I called a "forward incomplete type declaration." (*) Footnote: I claim that even Tucker's "with type" clause forces you to pull forward declarations out of a spec. At first glance, you might not think so. However, one of the difficulties people have had with Tucker's proposal is that it only gives you forward visibility to a single type. For example: with type Doctors.Doctor_Type; would give you forward visibility to the tagged type Doctor_Type (and its classwide type Doctor_Type'Class), but not to an auxiliary type like Doctor_Access_Type, or an ADT like Doctor_Sets instantiated off of that auxiliary type. Such auxiliary declarations might "belong" in the Doctors package, but to make them available without a full "with Doctors;" clause, you have to pull them out into another package: with type Doctors.Doctor_Type; with Sets; package Doctors_Access is type Doctor_Access_Type is access all Doctors.Doctor_Type'Class; package Doctor_Sets is new Sets (Element_Type => Doctor_Access_Type); end Doctors_Access; -- Signature volanSignature = new Signature ( /*name: */ "John G. Volan", /*employer: */ "Raytheon Advanced C3I Systems, San Jose", /*workEmail: */ "johnv@ac3i.dseg.ti.com", /*homeEmail: */ "johnvolan@sprintmail.com", /*selfPlug: */ "Sun Certified Java Programmer", /*twoCents: */ "Java would be even cooler with Ada95's " + "generics, enumerated types, function types, " + "named parameter passing, etc...", /*disclaimer:*/ "These views not packaged in COM.ti.dseg.ac3i, " + "so loading them throws DontQuoteMeError. :-)" );