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 X-Google-Thread: fac41,6b1d6fe1239c8614,start X-Google-Attributes: gidfac41,public From: "John G. Volan" Subject: Re: Naming of Tagged Types and Associated Packages Date: 1998/09/05 Message-ID: <35F15C7D.A33C272E@sprintmail.com> X-Deja-AN: 388178246 Content-Transfer-Encoding: 7bit References: <6qfp80$p0u$1@nnrp1.dejanews.com> <35CD0A8E.21D64380@sprintmail.com> <35CEBAAF.B9B82820@sprintmail.com> X-Posted-Path-Was: not-for-mail Content-Type: text/plain; charset=us-ascii X-ELN-Date: Sat Sep 5 08:44:33 1998 Organization: EarthLink Network, Inc. Mime-Version: 1.0 Reply-To: johnvolan@sprintmail.com Newsgroups: comp.lang.ada,comp.lang.eiffel Date: 1998-09-05T00:00:00+00:00 List-Id: [Cross-posted to comp.lang.eiffel, since I've added Eiffel content] Matthew Heaney wrote: > > "John G. Volan" writes: > [...snip...] > 3) Name scalar types using the units in the name, ie > > type Heading_In_Degrees is ...; [...snip...] > > 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); > > See my point above. > > The times when I care about the specific units of speed are only a > subset of all the times I manipulate a speed. When I really do care > about the units, I just navigate to the declaration, the read units from > the type name. > > You don't need to tell me the units every single time, because I don't > care about units every single time. And when I don't care, I have to > mentally parse it out. I'm not saying that such details need to be stated at _all_ times. I can accept dropping prefixes and suffixes if the context allows, e.g.: type Ship_Type is record Speed : Speed_In_Knots_Type; ... end record; Ship : Ship_Type; ... Ship.Speed ... -- we can look up the declaration to remind us of -- the units I'm merely saying that these suffixes and prefixes may be needed at _some_ time, when the context forces us to disambiguate things, as in my example you quoted above. If you had to do the above conversion using your naming scheme, how would you declare the two different "speed" objects? You rely on being able to drop a unit suffix to generate a variable name distinct from the type name. But in the above situation, these suffixes are needed again to disambiguate the two variables, which otherwise would have wound up with the same name according to your scheme. If your answer is, "Well, I'll just come up with some abbreviations for the speed in knots variable vs. the speed in mph variable," then I'd say, okay, you're free to do so, but then you'd have to admit that you were doing something ad-hoc. Remember, the reason we're forced into all these contortions is because of Ada's case insensitivity, combined with its strict insistence that type names and variable names share a single namespace (within a given scope). In a language that relaxes either of these properties, we don't have a problem. For instance, in Eiffel we might have: expanded class SPEED_IN_MPH inherit DOUBLE feature ... end expanded class SPEED_IN_KPH inherit DOUBLE feature ... end expanded class SPEED_IN_KNOTS inherit DOUBLE feature ... end class SHIP feature speed: SPEED_IN_KNOTS ... end -- class SHIP And the above conversion situation would be something like: speed_in_mph: SPEED_IN_MPH; speed_in_knots: SPEED_IN_KNOTS; ... speed_in_knots := to_knots (speed_in_mph) > > 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... > > I don't like this idea. The characteristics of the supporting type, > such as whether it's fixed or float, the digits of precision, range, > etc, are determined by the larger abstraction. So I usually place the > attribute types there, with the abstraction they describe. If I'm reading this right, what you're saying is that if an analysis of the problem domain indicates that a car has a speed in miles per hour, you'd put a Car_Speed_In_MPH type in the Cars package along with a _Car type? Suppose further analysis of the problem domain indicated that weather conditions constituted an important abstraction (let's say the system does fine engine and transmission control to improve traction and fuel economy in adverse weather). Then you might have a Wind_Speed_In_MPH type in a Weather package. Would these two speed types have no commonality? Would you be forced to do a lot of type-conversions to work with them both? Why couldn't you just have a single Speed_In_MPH_Type in a common package (regarding Speeds) that could be shared between the Cars package and the Weather package? I've often noted that Ada projects tend to proliferate a lot of unnecessary, redundant representations for the same classes of physical quantities. Little attempt is made to standardize on a normalized set of unit types and accuracies, so you wind up with a lot of complex, redundant, and hard to maintain conversion code scattered all over the system. Such projects might have benefited greatly if physical quantity types had been treated as first-class abstractions in their own right, e.g.: package Speeds is type Speed_Type is private; function From_MPH (MPH : in Long_Float) return Speed_Type; function From_KPH (KPH : in Long_Float) return Speed_Type; function From_Knots (Knots : in Long_Float) return Speed_Type; ... function To_MPH (Speed : in Speed_Type) return Long_Float; function To_KPH (Speed : in Speed_Type) return Long_Float; function To_Knots (Speed : in Speed_Type) return Long_Float; ... private type Speed_Type is record MPH : Long_Float; -- arbitrary choice, might be changed -- without affecting the abstraction end record; end Speeds; with Speed_Constants; use Speed_Constants; package body Speeds is function From_MPH (MPH : in Long_Float) return Speed_Type is begin return (MPH => MPH); end From_MPH; function From_KPH (KPH : in Long_Float) return Speed_Type is begin return (MPH => KPH * KPH_To_MPH); end From_KPH; function From_Knots (Knots : in Long_Float) return Speed_Type is begin return (MPH => Knots * Knots_To_MPH); end From_Knots; ... function To_MPH (Speed : in Speed_Type) return Long_Float is begin return Speed.MPH; end To_MPH; function To_KPH (Speed : in Speed_Type) return Long_Float is begin return Speed.MPH * MPH_To_KPH; end To_KPH; function To_Knots (Speed : in Speed_Type) return Long_Float is begin return Speed.MPH * MPH_To_Knots; end To_Knots; ... end Speed; Here's a similar rendition in Eiffel: expanded class SPEED inherit SPEED_CONSTANTS; -- assume provides Mph_to_kph, Kph_to_mph, etc... DOUBLE_APPROXIMATION -- assume provides comparison functions that account -- for numeric inprecision in DOUBLE computations, e.g.: -- sufficiently_equal, sufficiently_greater, etc. -- Are such functions already available somewhere? feature mph: DOUBLE -- this happens to be the only attribute, -- but that is an arbitrary choice. The rest -- are conversion functions: kph: DOUBLE is do Result := mph * Mph_to_kph end knots: DOUBLE is do Result := mph * Mph_to_knots end ... set_mph (new_mph: DOUBLE) is do mph := new_mph ensure sufficiently_equal (mph, new_mph) end set_kph (new_kph: DOUBLE) is do mph := new_kph * Kph_to_mph ensure sufficiently_equal (kph, new_kph) end set_knots (new_knots: DOUBLE) is do mph := new_knots * Knots_to_mph ensure sufficiently_equal (knots, new_knots) end accelerate (delta_speed: SPEED) is do mph := mph + delta_speed.mph ensure sufficiently_equal (mph, mph + delta_speed.mph) end ... invariant kph_definition: sufficiently_equal (mph * Mph_to_kph, kph); knots_definition: sufficiently_equal (mph * Mph_to_knots, knots); ... end -- class SPEED An intriguing alternative possibility would be to make SPEED a deferred class instead (an abstract type in Ada95 parlance): deferred class SPEED inherit SPEED_CONSTANTS; -- assume provides Mph_to_kph, Kph_to_mph, etc... DOUBLE_APPROXIMATION -- assume provides comparison functions that account -- for numeric inprecision in DOUBLE computations, e.g.: -- sufficiently_equal, sufficiently_greater, etc. -- Are such functions already available somewhere? feature mph: DOUBLE is deferred end kph: DOUBLE is deferred end knots: DOUBLE is deferred end ... set_mph (new_mph: DOUBLE) is deferred ensure sufficiently_equal (mph, new_mph) end set_kph (new_kph: DOUBLE) is deferred ensure sufficiently_equal (kph, new_kph) end set_knots (new_knots: DOUBLE) is deferred ensure sufficiently_equal (knots, new_knots) end accelerate (delta_speed: SPEED) is require delta_speed_exists: delta_speed /= Void deferred ensure sufficiently_equal (mph, mph + delta_speed.mph) end ... invariant kph_definition: sufficiently_equal (mph * Mph_to_kph, kph); knots_definition: sufficiently_equal (mph * Mph_to_knots, knots); ... end -- class SPEED Then, you could have different heirs of this class optimized for specific units of measure: expanded class SPEED_IN_MPH inherit SPEED feature mph: DOUBLE kph: DOUBLE is do Result := mph * Mph_to_kph end knots: DOUBLE is do Result := mph * Mph_to_knots end ... accelerate (delta_speed: SPEED) is do mph := mph + delta_speed.mph end ... end -- class SPEED_IN_MPH expanded class SPEED_IN_KPH inherit SPEED feature mph: DOUBLE is do Result := kph * Kph_to_mph end kph: DOUBLE knots: DOUBLE is do Result := kph * Kph_to_knots end ... accelerate (delta_speed: SPEED) is do kph := kph + delta_speed.kph end ... end -- end class SPEED_IN_KPH ... similarly for SPEED_IN_KNOTS, etc. Then a class like CAR could have a SPEED attribute that could avail itself of polymorphism: class CAR creation make feature -- Status report speed: SPEED feature -- Status setting set_speed (new_speed: SPEED) is require speed_exists: new_speed /= Void do speed := new_speed; end ... feature {NONE} -- Initialization make is do ... !SPEED_IN_MPH! speed -- default to optimize on miles per hour, -- but can be changed later (via set_speed) -- to optimize on other units ... end ... invariant speed_exists: speed /= Void ... end -- class CAR -- indexing description: "Signatures for John G. Volan" self_plug: "Ex Ada guru", "Java 1.1 Certified", "Eiffelist wannabe" two_cents: "Java would be even cooler with Eiffel's assertions/DBC, % %generics, true MI, feature adaptation, uniform access, % %selective export, expanded types, etc., etc..." class JOHN_VOLAN_SIGNATURE inherit SIGNATURE invariant disclaimer: not (opinion implies employer.opinion) end -- class JOHN_VOLAN_SIGNATURE