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=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,63a41ccea0fc803a X-Google-Attributes: gid103376,public From: "John G. Volan" Subject: Re: Naming of Tagged Types and Associated Packages Date: 1998/08/10 Message-ID: <35CEBAAF.B9B82820@sprintmail.com> X-Deja-AN: 379654279 Content-Transfer-Encoding: 7bit References: <6qfp80$p0u$1@nnrp1.dejanews.com> <35CD0A8E.21D64380@sprintmail.com> Content-Type: text/plain; charset=us-ascii Organization: EarthLink Network, Inc. Mime-Version: 1.0 Reply-To: johnvolan@sprintmail.com Newsgroups: comp.lang.ada Date: 1998-08-10T00:00:00+00:00 List-Id: Matthew Heaney wrote: > > "John G. Volan" writes: > > > (In fact, since P.D.'s example was Eiffel, the > > "STEERING_WHEEL" type would _be_ its own module). > > Your point highlights a difference between the two languages. In Ada, > it's very natural to declare related ("cohesive") types in a single > module. I was proceeding under the assumption that steering wheel type > didn't stand on its own; that it supported the larger car abstraction, > and hence was declared in the car module. > > This is a very common idiom in Ada, where you have a single module that > exports one or more primary ADTs, supplemented by auxiliary types that > describe attributes of the primary abstraction(s). Yes, this is an interesting idiom, and one may compare Ada's scheme of grouping cohesive types within Ada packages with Java's scheme of grouping cohesive classes within Java packages, or with Eiffel's scheme of grouping cohesive classes within clusters using the Lace language. However, this is off on a tangent from P. Doyle's question. P.D.'s question (and mine) was essentially this: You have a simple concept for some class of entities within your problem domain, and you have come up with a short, pithy noun phrase (e.g., "CAR", "STEERING_WHEEL") that names this concept within the context of your problem domain. Let us assume that the verbiage in this noun phrase is no more and no less than what is needed to convey the entire problem-domain concept you have in mind; in other words, the component words are at once necessary and sufficient. If that assumption is true, then removing any words would yield an abbreviation, which is insufficient; adding any words would create noise, which is unnecessary. Even words that convey the context of the problem domain are unnecessary, since such context can either be assumed, or expressed as module (package) names. All things being equal, this necessary-and-sufficient noun phrase is desirable both as an identifier for a type, and as an identifier for an (arbitrary) variable of that type. Every programming language requires some mechanism for distinguishing such identifiers from each other. Ada's mechanism is to force the identifiers to be literally different, which means either adding some kind of noise word to one or the other, or resorting to abbreviation for one or the other. Many react with distaste to either prospect, because they recognize both as catering to the programming language, and not to the problem domain concept we are trying to express. (Remember, the noun phrase in question is presumed to be both necessary and sufficient to express the concept.) Adding noise seems less obnoxious than abbreviation. So what's the least obnoxious noise you can add? > > So your answer begs the question: What, then, would you call an instance > > of the "CAR" type? > > > > In Eiffel, the answer is fairly simple: > > > > car : CAR ... > If it's the root of a tagged type hierarchy, then I would call it > Root_Car, just like they do in the RM (example: Root_Storage_Pool). I hope you recognize that "Root_" is as much a noise word as any other, because it caters to the mechanisms of the language rather than to the problem domain. Most OO programmers would find it distasteful to mark the class at the top of an inheritance hierarchy this way, simply because it was at the top of an inheritance hierarchy. But this is another tangent irrelevant to P.D.'s simple question, and it doesn't address the general case. What if, for the particular problem domain at hand, "CAR" is _not_ intended as the top of an inheritance hierarchy? In fact, what if we even wanted to _preclude_ any inheritance? (In Ada terms, the Car type would be non-tagged. In Java, it would be a final class. In Eiffel ... not sure of the syntax.) In that case, attaching the "Root_" prefix would not only be noise, it would be misleading noise. So the question remains: All things being equal, what's the least noxious noise you can add, that will work even in the most general case? > ... I think it's confusing to use > expanded name notation to differentiate between otherwise identical > names for object and type. Use different identifiers for each. On this point, I agree. > > It seems to me that if one is forced to come up with distinct > > identifiers for a type and an (arbitrary) instance of that type, then > > the difference between the identifiers should reflect this base/meta > > distinction. Thus, the Cohen style: > > > > Car : Car_Type; > > Sometimes, if there really is no suitable adjective to describe the type > (or, you're just not feeling too creative that day), then you do have to > use _Type as the type name. But I find those times to be rare. Again, I take exception to the notion that it is "uncreative" to be systematic. I'd rather focus my creativity on the terminology of the problem domain and not waste it on irrelevant artefacts of the coding process. But I find it interesting that you fall back to my position as a sort of default: When all else fails, use the base/meta (object/type) distinction as a source for the extra noise. But otherwise, be "creative": Derive the noise, if you can, from some aspect of your program design that's irrelevant to the problem domain (e.g. add "Root_" to "Car"); or in other cases, redundantly express context that might already be expressed in the module structure (e.g. add "Car_" to "Steering_Wheel", even though it's already nested in a "Cars" package). What other cases do you propose? With all these various cases, isn't your scheme really ad hoc? If you claim it is not ad hoc, can you articulate a finite number of rules that cover all situations (or at least a sufficient majority of them)? The fewer rules the better, because it means less effort for code writers to apply them, less effort for code readers to determine which rule has been applied in a particular situation, less effort for maintainers to keep straight what they're talking about when they converse about the code, etc. Personally, I find the form of "creativity" you advocate to be dubious. I know of no analog for your scheme in the idioms of any other programming language. Why not just settle on a simpler and more systematic way to introduce the required noise, and have done with it? The base/meta distinction is always there, in every case. In fact, it is the entire reason for this naming dilemma in the first place: We have objects, and we have their types, and Ada requires that they have different identifiers. So why not just have a single rule: Consistently mark every type as such, and reserve the unmarked noun phrase for a possible variable name. It's how people speak about the code anyway: "... and in this method we have an argument 'car' which is an instance of the 'car' type, which we're using to ..." > > The old Booch style: > > > > The_Car : Car; > > > > also reflects this same base/meta distinction. > > I don't like this convention because it adds noise that the human reader > is going to mentally parse out anyway, so why not parse it out for him? But the whole point is that every scheme introduces noise. We are only quibbling over which form of noise is the least distasteful. I could as easily say that the human reader is only interested that the thing is a "Car", and will just parse out the "Root_" part anyway; or that the thing is a "Steering_Wheel" from the "Cars" package, and will just parse out the "Car_" part anyway; so why not just parse out these noise words for him? > Better to name the type Car_Type, and the instance Car, becuase the > object is going to be refered to more often than the type itself. Yes, I agree that there is a strong argument in favor of putting the burden of marking upon the type identifier, in order to keep variable names as short as possible. However, when you add packages into this naming dilemma, it seems as if a Booch-like style is still somewhat compelling, as J-P Rosen's style suggests: The_Car : Car.Instance; The_Steering_Wheel : Steering_Wheel.Instance; Booch style gives the unmarked noun to the type; Rosen style gives it instead to the package, and the encapsulated type gets a systematic noise word. It would have been nice if this word could have been just "Type": The_Car : Car.Type; The_Steering_Wheel : Steering_Wheel.Type; but of course "type" is a reserved word. (I once toyed with "Tipe", but thought better of it ... :-) > A new member of the team I'm on declared a bunch of types (it was a > binding to a tape drive API) all using the _T convention. I pointed out > to him that perhaps a better name would be to include the tape drive in > the type name, instead of using _T. > > His rebuttal was to ask me how I'd name a color type. My immediate > response is, "Well, what is it a color of?" He said, "Say, a car." So > my advice was, name the type Car_Color, not Color_Type. But you see, you diverted the poor guy off onto a tangent. What if he had responded, "Why, it's a color of anything that can _be_ colored, not just the color of a car. In short, "Color" is an abstraction all its own, in a module of its own that is all about colors. Please don't pollute that abstraction by making it specific to just one particular usage... Now, this abstraction is about "Color" and only about "Color". I want to call the package "Color". But I also want to call the encapsulated type "Color". And I want to call the controlling parameter "Color" in each of my primitve operations, too. How do I resolve this dilemma?" > Nor is the object name ad hoc: simply remove the adjective part of the > name, and keep the noun part. Just like Mode is an instance of type > File_Mode, I don't think the original designers of Text_IO necessarily completely thought through all the issues of naming conventions back then. Isn't the "File_" part in "File_Mode" just a redundant re-iteration of the context, since we know that Text_IO is all about text files anyway? (I sometimes wish Text_IO had been named Text_Files instead, as this would have been a more "object-oriented" name; "Text Input/Output" is the _function_ this package supports, but "Text Files" are the _objects_ this package is _about_.) On the other hand, let's suppose "File_Mode" is a necessary and sufficient phrase to express the whole sense of the concept (presumably within a use-ophilic environment). Maybe you can get away with dropping the "File_" adjective for a variable within the package, but can you always do that outside the package, in all its conceivable clients? What if there's a context where you have both a "File_Mode" and some other kind of mode, say an "Operational_Mode" (e.g., Real vs. Simulated)? Then it becomes important to use the complete names for those concepts, including the "File_" and "Operational_" prefixes, not just in their type names but also in the variable names. If we now need "File_Mode" and "Operational_Mode" as variable names, what should the types have been called? You know, I'm not averse to the idea, per se, of dropping prefixed adjectives when they can be understood from the context. In fact, in object-oriented software, it's often the case that a subclass name will be formed by prefixing an appropriate adjective onto its superclass name. A variable of the subclass type can just as easily be thought of as a variable of the superclass type, so it can be legitimate to just refer to the variable using the simpler terminology of the superclass type. I'm just against the idea that dropping prefixed adjectives offers a general solution to the object/type name dilemma. One can always come up with cases where you need to put those adjectives back in, or where there are no appropriate adjectives to drop in the first place. > Another simple example: I like to name all scalar types with a dimension > by using the units in the name, as in > > Speed : Speed_In_MPH; > Length : Length_In_Meters; > > This solves the object-name type-name issue, and removes any ambiguity > about what units apply to the scalar type. As I've pointed out in the past, if it's important to say something in a type name, there could easily be a case where it's important to say it in the variable name too: Speed_In_MPH : Speed_In_MPH_Type; Speed_In_Knots : Speed_In_Knots_Type; ... Speed_In_Knots := To_Knots(Speed_In_MPH); > When refering to a literal, > I like to use a qualified name, as in > > Set_Speed (Car, Speed => Speed_In_MPH'(10.0)); How about just: Set_Speed (Car, Speed_In_MPH => 10.0); Or perhaps we should consider the word "Speed" to be unnecessary noise: MPH : MPH_Type; Knots : Knots_Type; ... Knots := To_Knots(MPH); ... Set_Speed (Car, MPH => 10.0); Perhaps "Speed" could be the name of the package that houses the MPH_Type and Knots_Type, and the To_Knots function, etc... > > If it was important enough to say, in a type name, that something is a > > "Car_Steering_Wheel" (as opposed to, perhaps, a "Truck_Steering_Wheel"), > > then why isn't it also important to say "Car_Steering_Wheel" in the > > variable name too? Why arbitrarily lop off the "Car_" part? > > Because you only need to tell me once about the nature of the > abstraction. We all know, by reading the _entire_ declaration (not just > the object name), that we're talking about the steering wheel of a car. > It _is_ important to tell my that this is a car steering wheel, but you > only need to tell me once (at declaration time), not tell me again and > again and again every time I refer to the object. Because then it would > just become information the reader is going to mentally parse out. Unless there's a situation where it becomes important again to distinguish it as a car's steering wheel, and we find we need to put that adjective back: Car_Steering_Wheel : Car_Steering_Wheel_Type := ... Tow_Truck_Steering_Wheel : Tow_Truck_Steering_Wheel_Type := ... ... -- make the Car simulation physically follow the Tow_Truck simulation: Set_Direction (Car_Steering_Wheel, Get_Direction (Tow_Truck_Steering_Wheel)); > > On the other hand, if the "Car_" part is already understood (because the > > type is already nested inside a "Cars" package), then why bother with > > the "Car_" part in the first place, in the type name? Isn't that just > > noise? > > The Car_ part is there because we need to have a different name for the > type, because we want to call the object Steering_Wheel. So basically you're saying the "Car_" part is just noise, right? > To use _Type really would be noise, because that doesn't tell me > anything I don't already know (even though it does satisfy the need to > use a different identifier for the type). But "Car_" doesn't tell me something I don't already know, either, right? I mean, to understand the entire abstraction, we don't just look at a single _declaration_, we look at the whole _package_ that encapsulates it, right? The package name "Cars" already says it all. > If we know that the object is going to be called Steering_Wheel, and > that we need a different (and longer) name for the type, then you might > as well add additional, substantive information about the abstraction > the type describes. But what substantive information are you adding that hasn't already been supplied in other ways? with Cars; -- assume this is written by a use-o-phobe ... My_Cars_Steering_Wheel : Cars.Car_Steering_Wheel; ^^^^^^^^ Isn't this redundant, and therefore noise? > (Note that I'm proceeding here under that assumption that types don't > just float around in space; they're there for a reason, and steering > wheel abstractions "go with" car abstractions. As they say, "No object > is an island.") Some objects are indeed islands, if we choose to keep them uncoupled to other objects. Making abstractions "go with" each other in a single package increases coupling. That's a design choice. The benefits of cohesion sometimes outweigh the cost of the coupling, but nevertheless the coupling does incur a cost. Sometimes the cost of coupling is too dire, so the right design choice is to separate things out. > > A naming convention should be systematic, mechanical, predictable and > > transparent. That means that it should take NO THOUGHT to apply it, or > > to interpret the results. > > Be careful not to assume that God decided that this is so. This > description of a "good" naming convention is a choice YOU made. > > In fact, I take the exact opposite position, and think that type names > require careful consideration of the application, the nature of the > abstraction, and of the intended names of instances. How is this the "opposite" of what I have been saying? Of course you should take great care in choosing names that express the problem domain _concepts_ you wish to address in your program. There is a great deal of care, consideration, and indeed creativity that goes into this design process. But once you have found names for your concepts, rendering those concepts into types and variables should be as direct and straightforward as possible. If your programming language forces you to spend additional creative energy just to cater to its syntactic foibles, that's a waste. It would be as much of a waste to spend time deciding what noise word to add, just for the sake of differentiating a type name from variable name, as it is to spend time figuring out that: if (x = y) ... really should have been: if (x == y) ... > I'm not suggested that mechanical application of a naming convention > limits a programmer's creativity. In fact, the convention I advocate > often can be applied mechanically. You seem to be contradicting yourself. How can your convention be applied mechanically if by definition it requires creativity? If your convention can be expressed as a finite set of rules that cover all situations, then by definition it would require no creativity (beyond the initial creativity needed to give names to problem domain concepts, of course). > Please don't call this the Heaney style - call it the RM style. My > argument isn't for a naming convention per se, it's that we should stick > to the conventions already being used in the language reference manual. There appear to be many different conventions demonstrated within the RM, possibly as many different styles as there were designers on the design teams. Perhaps the RM can be viewed as a forum where precedents for various styles were recorded, but I don't think the intent of the RM was ever to establish some particular coding style as the ideal. Its primary intent, after all, was to unambiguously specify what the language is, not to prescribe how it should be used. Precedent, even long precedent, should not be the only criterion for chosing a coding style. I would think it's more important to base the choice on a well-thought out rationale. As much as we venerate the wise men of ancient days, they really were no closer to God than we are now. But we have had more time to think about it than they did. -- Signature volanSignature = new Signature ( /*name: */ "John G. Volan", /*employer: */ "Raytheon/TI 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, named parameter passing, etc...", /*disclaimer:*/ "These views not packaged in COM.ti.dseg.ac3i.*, " + "so loading them throws DontQuoteMeError. :-) " );