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,ae776ae9b3416efc X-Google-Attributes: gid103376,public From: mheaney@ni.net (Matthew Heaney) Subject: Re: Package name qualifier on function? Date: 1998/02/06 Message-ID: X-Deja-AN: 322824108 Content-Transfer-Encoding: 8bit References: <34DB9059.7E4E@mcc.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Organization: Estormza Software Newsgroups: comp.lang.ada Date: 1998-02-06T00:00:00+00:00 List-Id: In article <34DB9059.7E4E@mcc.com>, Lane Wimberley wrote: >package Foo is > type internalfoothing is private; > function create return internalfoothing; >private > type somethingelse; > type somethingelseptr is access somethingelse; > type internalfoothing is new somethingelseptr; >end Foo; > >-- spec of different package that withs Foo package: >with Foo; >package Bar is > type FooThing is private; >private > type FooThing is new Foo.internalfoothing; >end Bar; > >-- body of different package >package body Bar is > >function create(param : someparamtype) returns FooThing is > newfoothing : FooThing; >begin > newfoothing := create; -- why is this OK? > return newfoothing; >end create; > >end Bar; > >As the comment indicates, I can't figure out why the compiler >accepts the invocation of "create" without any package qualifier. >I would have expected to see "Foo.create" Also, why do I get no >complaint about the fact that "create" returns an InternalFooThing >yet newfoothing is of type FooThing? You must understand the concept of "type," and the concept of a "primitive operation" of a type. In any system, there are probably thousands of subprograms. Some of these subprograms have as a parameter an object of type T (say), and maybe some others are functions that return an object of type T. Among this subset of all the subprograms in the system, there's another subset, the subset comprising the "primitive operations" of type T. The larger subset are all "operations" of type T, but not all of them are "primitive" operations. Let type T be declared in package P: package P is type T is range 2 .. 5; procedure Op1 (O : T); function Op2 (I : Integer) return T; end P; with P; package Q is procedure Op3 (O : P.T); function Op4 (F : Float) return P.T; end Q; There are 4 total subprograms - "operations" - that take T as a parameter or return value. Among this group of subprograms are 2 that are "primitive operations of type T": Op1 and Op2. The rule is that the subprograms that take an argument of the type or return a value of the type, and are declared in the same package as the type declaration itself, are "primitive operations" of the type. Operations Op3 and Op4 do take a parameter of type T or return a value of type T, but these operations are not "primitive operations" in the sense of Op1 and Op2. The reason we make this distinction is that primitive operations of a type - and only primitive operations - are inherited by a type that derives from that type. For example, let's derive a type from T: with P; package R is type NT is new P.T; end R; There are 2 "primitive operations" for type NT. It's as if package R were with P; package R is type NT is new P.T; procedure Op1 (O : NT); function Op2 (I : Integer) return NT; end R; When NT derived from T, the primitive operations of T came along for the ride. We say that Op1 and Op2 are "implicitly declared" immediately after the declaration of type NT. This makes sense when you consider what a "type" is. A "type" is 1) a set a values 2) a set of operations The type T comprises 1) the integer values 2, 3, 4, 5 2) the "predefined" operations +, -, *, /, etc, and the "user-defined" (I don't know if that's the exact technical term) operations Op1 and Op2. When NT derives from type T, it (essentially) denotes the same values as T and the same operations of T. Many Ada programmers don't understand the subtlety of this. When you say "type T" programmers often think that it means the same thing as the statement "type T is range 2 .. 5;". Not quite. The statement type T is range 2 .. 5; is actually a "binding" operation, that binds a name, here "T", to a type, comprising the values 2, 3, 4, 5, and the operations +,-, etc, Op1, and Op2. Ever here of the expression "The map is not the territory"? Well the same principle is at work here. "T" is the *name* of a type, it is not the type. Nor is the statement the declares the type, the type. The "type" is the set of values and the set of operations. Many programmers think that package P declares "a type and 2 subprograms" but that's not quite right. Package P declares a type. Period. The type comprises the values and operations, and a name is bound to that type. When type Bar.Foothing derived from Foo.internalfoothing, it's as if the declaration were with Foo; package Bar is type FooThing is private; private type FooThing is new Foo.internalfoothing; function create return foothing; end Bar; Because type FooThing derives from Internalfoothing, it inherits the "primitive" operations of that type, here operation create. And that's why operation create is directly visible in the body of package Bar, because it's a primitive operation of FooThing, implicitly declared immediately are the derivation from internalfoothing. This concept is import for object-oriented programming too. During a derivation, the tagged derived type inherits the primitive operations of the parent tagged type. The difference between your example and true "object-oriented" programming is that in "object-oriented" programming you can "extend" the type; that wasn't possible in Ada 83. Some authors make the distinction between "inheritence" and "type extension." That's why you'll often hear Ada 95 described as adding "type extension" to the language, because "inheritence (of operations)" was already in the language. A lot of people who denigrated Ada 83 because it was "merely" object-based are sort of missing the boat, because Ada 83 already had a lot of powerful features for creating abstractions. The fact that it didn't have type extension shouldn't have been such a big deal (it wasn't for me) because type extension and polymorphism are only ADJUNCTS to data abstraction. It's direct support for data abstraction that's important, not inheritance (type extension) and polymorphism. To measure a language by its support for inheritance and polymorphism, and ignore support for data abstraction, is backwards thinking. Hope that clears things up. Matt