leake@cme-durer.ARPA (Stephe Leake) (11/29/88)
In article <7796@aw.sei.cmu.edu> dd@sei.cmu.edu (Dennis Doubleday) writes: In article <739@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes: >In article <7882@nsc.nsc.com> rfg@nsc.nsc.com (Ron Guilmette) writes >> I have some problems with limited private types too. Specifically, if such >> types are really only accessable through a "limited" functional interface, >> then why shouldn't we be able to declare such types in the public part of >> a package and then defer the full declaration until the package body? > >What would this gain? Do you have any specific examples where this >would have improved the readability or functionality of an >application? Let's not just suggest changes because they "sound good". >Let's put some thought into it, including concrete examples. There is a VERY good reason for it, if you're at all concerned about portability. The declaration of a limited private type is an IMPLEMENTATION decision. It doesn't belong in the specification, it belongs in the body. If I'm trying to export some abstract data type from a package, I'd like to have a specification that could be moved, intact, from one machine to another. Only the body should need changing. However, since I must give the full declaration of the type in the private part of the spec instead of in the body, it may be that I will have to change that part of the spec if I want to port the package to a machine which requires a different implementation of the type. This is true, but it doesn't allow separate compilation; you cannot compile bodies that use the limited private type untill the size of that type is known. One way around this is to declare the limited private type to be a pointer to a type whose full declaration is defered to the body of the package: package My_Private_Type type Hidden is limited private; ... private type Real_Hidden; type Hidden is access Real_Hidden; end My_Private_Type; Admittedly, access types are not the best in all situations, but this does enhance portability. >Why does your post sound so hostile? How do you know that Ron hasn't >put any thought into his suggested changes? I didn't mean to sound hostile, I was simply frustrated with the lack of information. If Ron was concerned with the issue you raised, he should have said so explicitly. Believing someone has good reasons, and knowing what those reasons are, are two different things. Stephe Leake (301) 975-3431 leake@cme.nbs.gov National Institute of Standards and Technology (formerly National Bureau of Standards) Rm. B-124, Bldg. 220 Gaithersburg, MD 20899
rfg@nsc.nsc.com (Ron Guilmette) (12/02/88)
In article <748@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes: > >In article <7796@aw.sei.cmu.edu> dd@sei.cmu.edu (Dennis Doubleday) writes: > > In article <739@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes: > >In article <7882@nsc.nsc.com> rfg@nsc.nsc.com (Ron Guilmette) writes > >> I have some problems with limited private types too. Specifically, if such > >> types are really only accessable through a "limited" functional interface, > >> then why shouldn't we be able to declare such types in the public part of > >> a package and then defer the full declaration until the package body? >This is true, but it doesn't allow separate compilation; you cannot >compile bodies that use the limited private type untill the size of >that type is known. One way around this is to declare the limited >private type to be a pointer to a type whose full declaration is >defered to the body of the package: OK. Now there no >'s in from so this is me (Ron Guilmette) talking again. Sorry for all the >>>>'s above, but I just couldn't pass up the chance to totally confuse the issue by quoting quotes of comments on quotes of comments of what I said way back when. :-) Anyway, I *do* know about the special tricky Ada rule which allows you to defer a full type definition until the corresponding package body (if that type is only used in the package spec to help define pointer types). >Admittedly, access types are not the best in all situations, but this >does enhance portability. > > >Why does your post sound so hostile? How do you know that Ron hasn't > >put any thought into his suggested changes? > >I didn't mean to sound hostile, I was simply frustrated with the lack >of information. If Ron was concerned with the issue you raised, he >should have said so explicitly. Believing someone has good reasons, >and knowing what those reasons are, are two different things. Also, although I didn't explicitly say it, I did mean to make a point about the "separation of concerns" between a spec and a body, which Ada generally supports very well. (I don't think that "portability" per se is quite the issue that I'm concerned about). Anyway, don't worry about apparent hostility. I'm generally pretty thick skinned (and some would say thick-headed). The point I wanted to make was this. If you look at all the things that you are allow to do with an object of a limited private type (as given in LRM 7.4.2 and 7.4.4) in the scope where the given type is *actually* limited (i.e. within the *whole* scope of the type name but *not* in the same scope as the full type definition for the type) then you find out that there isn't very much you can do with (or to) an object of a limited private type. Basically, about the only thing you can do with such objects is to pass them as actual parameters to subprograms. Obviously, if you do this, then for each such subprogram, the actual *definition* of the given subprogram must be compiled only *after* the full type declaration has already been compiled. "Why?" you ask. Well it is because of the fact that Ada insists that scalars must always be passed by value-result. Non-scalars may be passed any old way (e.g. by reference). Since any given Ada compiler must generate code to copy scalar actual parameters into their corresponding formals, and since the most efficient way to do this is to know ahead of time the total size (e.g. 32 bits) of the given parameter, and since the full type definition of a given limited private type (object) may sometime be scalar, The Ada designers made the only wise choice available to them, i.e. to insist that the full type declaration for any arbitrary limited private type be given in the same compilation (unit) as the (original) limited type declaration. This rule insures that the compiler will always know the actual size of all formal parameters at least before the end of the current compilation. Thus, the "scalar-by-value-result" rule (see LRM 6.2{6}) may be obeyed while parameter passing is kept efficient. What I was proposing was the suspension of the "scalar-by-value-result" rule, and a substitution (in LRM 6.2{6,7,8}) of language which would instead insist that *all* parameters, regardless of type, be passed by reference. This would yield two benefits: 1) We could put our (corresponding) full type declarations for our limited types into packages bodies where they seem to belong (and where they require fewer re-compilations when changed) and 2) we could make the language simpler by having one single (standard?) type of parameter passing *semantics* for *all* types of objects. Another (dubious?) benefit of my "pass-by-reference-only" rule would be that it would make all the ex-FORTRAN people feel comfortable (and would probably simplify mixed-language interfacing where Ada & FORTRAN were concerned). Note that FORTRAN has *always* used the "pass-by-reference-only" rule. Note that the programmer himself can easily achieve the semantics of "pass-by-value-result" if he/she is given the base semantics of "pass- by-reference" but not vise versa! To get value-result semantics given a base of by-reference semantics, all one has to do is make one's own temporary copies (of the variable(s) involved) within the subprogram in question.
sommar@enea.se (Erland Sommarskog) (12/04/88)
Ron Guilmette (rfg@nsc.nsc.com.UUCP) wants to be able to defer the implementation of a (limited) private type to the body of his package. He writes: >Anyway, I *do* know about the special tricky Ada rule which allows you >to defer a full type definition until the corresponding package body >... >Basically, about the only thing you can do with such objects is to pass >them as actual parameters to subprograms. Obviously, if you do this, then >for each such subprogram, the actual *definition* of the given subprogram >must be compiled only *after* the full type declaration has already been >compiled. >... >Since any given Ada compiler must generate code to copy scalar actual >parameters into their corresponding formals, and since the most efficient >... Since you have thought so much on the not-so-obvious problem of parameter passing, you must have found the answer to the more obvious problem of heap and stack allocation as trivial. I am probably stupid, because I fail to see any attractive solutions to that. If in my block I declare: A : A_limited_type; B : Another_limited; C : ARRAY(1..20) OF Yet_another_limited; If the implementation of the types in available in the specification of the types originating package, there is no problem. But if you defer them to the body, and do not allow cheating with pointers? Enlighten me, how to you allocate them on the stack? Implicit pointers? Some implicit size-telling function called at run-time? Not very attractive, since the place on the stack for other local variables is dependent of the size of the types. To be straight I think that the pointer "trick" serves our needs perfectly here, and that your proposal is just YAUAC. (Yet Another Unnecessary Ada Change.) -- Erland Sommarskog ENEA Data, Stockholm sommar@enea.se "Frequently, unexpected errors are entirely unpredictable" - Digital Equipment
stachour@umn-cs.CS.UMN.EDU (Paul Stachour) (12/05/88)
In article <8197@nsc.nsc.com> rfg@nsc.nsc.com.UUCP (Ron Guilmette) writes: > >What I was proposing was the suspension of the "scalar-by-value-result" >rule, and a substitution (in LRM 6.2{6,7,8}) of language which would >instead insist that *all* parameters, regardless of type, be passed >by reference. > >This would yield two benefits: > >1) We could put our (corresponding) full type declarations for our > limited types into packages bodies where they seem to belong (and > where they require fewer re-compilations when changed) and > >2) we could make the language simpler by having one single (standard?) > type of parameter passing *semantics* for *all* types of objects. > >Another (dubious?) benefit of my "pass-by-reference-only" rule would be >that it would make all the ex-FORTRAN people feel comfortable (and would >probably simplify mixed-language interfacing where Ada & FORTRAN were >concerned). Note that FORTRAN has *always* used the "pass-by-reference-only" >rule. I have programmed in a variety of languages over the 25 years (wow, it is hard to belive, but it is 25 years) that I have been programming. In that time, I've learned a LOT about parameter-passing. I learned about Multics and good PL/I implementatations only after I already had 15 years of work, like 10 years ago. One of the things I discovered was how efficent its parameter-passing was, as well as being flexible. PL/I likes to pass everything by reference, and that's good (I'll say why in a moment). Multics uses 2n+1 (or n+1) words for each parameter-list. nwords for arg-pointers, nwords for arg-desicriptors (iff needed), and 1-word for the arg-count, discriptor-count, and whether args or discriptors exist at all. Functions get an extra-arg (the result) created in the caller. This means, in general, that one can often use an arg-list fully prepared at compilation-time (sometime modified at execurion-time), and just pass one-pointer to a calling routine. Long and well-thought Ada-style paramater-lists (including defaults) can be done very well using this mechanism. But the real advantage of reference semantics comes when you have data-structures being accessed by multiple tasks, as we are now starting to do in Ada, and Multics has done well for more than the 10 years I've known it. You pass an argument to a subroutine, it changes it, and the change is immediately, instantly, visable to all routines. And without the messiness of passing explicit pointers. Now, languages that don't allow reference semantics at all are really ugly. C's sometimes-you-must (stucts) and sometimes-you-mussent (arrays) are confusing. Ada's consistancy (at least for composites) is refreshing after the last two years I've been fighting C to get jobs done. However, I don't believe that Ada REQUIRES reference semantics for composites, it merely allows them. And in these days of distributed programs, it is questionable what kind of efficiency one can get with reference semantics over many machines. [The questions are similar to those of efficiency in virtual-machines 20 years ago; questions which were well-settled by the measurements in Multics showing that IO actually got cut in half in virtual-memory + sharing paradym over thant of physical-memory + explicit IO. Too bad most hardware/software groupings we call computer systems still can't seem to do good virtual-memory management, but that's another discussion.] I'm for reference semantics. They are natural, efficient, and don't force this explicit-pointer "junk". But let's make sure what we can do with them in today's environment before we ask Ada 9X to mandate them.
ryer@inmet.UUCP (12/07/88)
I agree it would be nice to be able to defer specifying the details of the private type until the body is compiled. Also, the reason for the present restriction is so that the compiler can know the size of the type. Implicitly generated heap objects would work ok (forcing all passage of private types by reference). HOWEVER: Ada now provides the pointer-based semantics when the user explicitly codes an access type. It ALSO provides the more efficient direct allocation/access when the user does not code a access type. The language changes proposed would eliminate one option. It is possible to build an Ada compiler which NEVER uses the heap or any sort of pointer to access ordinary objects of static sizes, and at least one is very careful of this. This is important to those who use Ada for realtime systems. Many actual private types in real programs turn out to be integers. Passing these by value makes a big difference in the performance of the generated code. (Often a procedure parameter is already in a register at the call site, and some optimizers contrive to make it the appropriate one so the cost for passing it is zero as compared to a store at the call site followed by a load in the caller). Some Ada applications are built for small machines with restricted memory and modest throughput and have to meet timing constraints that are very tight (e.g. 100 hertz). As we used to say in the Ada language design days, "any good compiler will resolve this", in this case by only introducing an implicit access type when the private type's definition is missing from the spec and otherwise generating the same code as now. I hope we've learned that compiler writers sometimes take the easiest path and defer the optimal approach to some future release. We've gone through years of performance problems with Ada because of compilers that weren't optimal. Efficient approaches were proposed for every language feature when the language was being designed, and features were dropped it nobody could come up with an efficient implementation. It turns out that compilers aren't as good as the language designers contemplated even now. There had been no mention of the impact on the generated code in this note stream and I had to post this flame. In the 9X cycle, we need to ask not only whether an efficient implementation is feasible, but whether it is likely to appear in most compilers within a reasonable time. I "vote" strongly against the proposal that ALL parameters should be passed by reference -- that clearly would make Ada less efficient on most machines however good the compilers are. The slow acceptance of Ada has been due to performance problems in the generated code and speed and reliability problems in the compilers, not due to a lack of flexibility in dealing with private types or other features. Lets not add complexity and reduce performance this time around. Mike Ryer Intermetrics, Inc.
stt@inmet (12/07/88)
All Ada compilers already deal with "stack" objects of size unknown at compile time (e.g. x : string(1..f(y));). Some compilers do this more efficiently than others, but the big advantage is that the compiler takes care of freeing the associated storage when the object goes away. When access types are used, it is always a challenge to figure out how to ensure that the storage gets efficiently reclaimed. (This is one of the big reasons why I believe user-defined "finalization" should be added into the language.) Nevertheless, access types with a deferred incomplete designated type are still used in complex systems, because they break recompilation dependencies. It seems like allowing the deferral of any private type to a package body would be little or no additional burden on compiler writers, and would provide the same nice break in recompilation dependency when used. For non-discriminated types, the size would be an elaboration-time constant. For discriminated types, the size would be determined by a function call (many compilers already create size functions to save code space for complex variant record size calculations). However, it would be appropriate to require that the full definition of the private type be a record type, so that pass-by-reference semantics would be allowed. The syntax for such a deferred private record type might be: . . . private type Priv is record; . . . I fully agree with the importance of pass-by-value semantics for all scalars/access-values. It is easy enough to wrap a scalar into a record if pass-by-reference semantics are preferred for some particular application. The improved speed possibilities for pass-by-value scalars far outweigh any abstraction disadvantage. S. Tucker Taft Intermetrics, Inc. Cambridge, MA 02138
leake@cme.nbs.gov (Stephe Leake) (12/07/88)
In article <8197@nsc.nsc.com> rfg@nsc.nsc.com (Ron Guilmette) writes:
What I was proposing was the suspension of the "scalar-by-value-result"
rule, and a substitution (in LRM 6.2{6,7,8}) of language which would
instead insist that *all* parameters, regardless of type, be passed
by reference.
This would yield two benefits:
1) We could put our (corresponding) full type declarations for our
limited types into packages bodies where they seem to belong (and
where they require fewer re-compilations when changed) and
2) we could make the language simpler by having one single (standard?)
type of parameter passing *semantics* for *all* types of objects.
This sounds reasonable, but will obviously have a performance impact
where lots of scalar parameter passing is going on (something I don't
do much - records are much more expressive than scalars). But since
the problem is really only with (limited) private types, how about
insisting only those must be passed by reference?
Stephe Leake (301) 975-3431 leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD 20899