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,2ea02452876a15e1 X-Google-Attributes: gid103376,public X-Google-Thread: fac41,c52c30d32b866eae X-Google-Attributes: gidfac41,public X-Google-Thread: 1108a1,c52c30d32b866eae X-Google-Attributes: gid1108a1,public From: donh@syd.csa.com.au (Don Harrison) Subject: Re: Real OO Date: 1996/05/02 Message-ID: X-Deja-AN: 152559263 sender: news@assip.csasyd.oz references: organization: CSC Australia reply-to: donh@syd.csa.com.au newsgroups: comp.lang.eiffel,comp.lang.ada,comp.object Date: 1996-05-02T00:00:00+00:00 List-Id: Jon S Anthony writes: :In article donh@syd.csa.com.au (Don Harrison) writes: : :> :> Can you explain how these avoid the problem? : :First, this is a really bad design (even for an example) as it :violates any of the (several) reasonable definitions of is-a :semantics. So, you're saying is car and truck drivers are not drivers and cars and trucks are not vehicles. Right? I can see that. : Second, this example has nothing to do with "legacy" code :or reuse (which is what you claimed was important and I was refering :to). Yes, my original statement was based on a misunderstanding that routines could be redefined with non-conformant parameters. If this were true, then the statement would have been true. No, the issue of system validity arises because certain rules are relaxed to allow legitimate reuse of existing software. : But, let's take it at face value. Neither the a) no b) case is :a problem - not even an issue - in Ada. This is mainly due to the :separation of specific and classwide types. Your later example is closer semantically to my Eiffel example, but a few comments are in order: : :The example in Ada: : :package Drivers is : type Driver is tagged private; :.... :end Drivers; : :package Drivers.Cars is : type Car_Driver is new Driver with private; :.... :end Drivers.Cars; : :package Drivers.Trucks is : type Truck_Driver is new Driver with private; :.... :end Drivers.Trucks; : : : :with Drivers; use Drivers; :package Vehicles is : type Vehicle is tagged private; : : procedure Register_Driver (V: Vehicle; D: Driver); : procedure Renew_Rego_By_Mail (V: Vehicle); :.... :end Vehicles; : :with Drivers.Cars; use Drivers.Cars; :package Vehicles.Cars is : type Car is new Vehicle with private; : : procedure Register_Driver (V: Vehicle; D: Car_Driver); :.... :end Vehicles.Cars; : :with Drivers.Trucks; use Drivers.Trucks; :package Vehicles.Trucks is : type Truck is new Vehicle with private; : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); :.... :end Vehicles.Trucks; : : :-- In a client somewhere :-- : D: Driver; : Cd: Car_Driver; : V: Vehicle; : T: Truck; :.... : -- Option 1. : -- : D := Cd; -- Illegal, compile time error : V := T; -- Illegal, compile time error Okay. : -- Option 2. : -- : D := Driver(Cd); -- OK, convert toward root : V := Vehicle(T); -- OK, convert toward root : Register_Driver(V, D); -- just fine and no dispatch needed Not okay, if the objects get truncated in the type conversions. :-( Are they? Also, is it legal to write: V : Vehicle'Class; ... T := Truck(V); without supplying an aggregate containing the missing attributes? If both of these are legal, both may result in inconsistent objects being assigned. Neither is possible in Eiffel. : -- Option 3. : -- : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops : : -- Option 4. : -- : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops : : -- Option 5. : -- : Register_Driver(Car'Class(T), Cd); -- Compile error, trucks not in : -- Car'Class All okay. : -- Option 6. : -- : Register_Driver(Vehicle'Class(V), D); -- Fine : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) Not okay, because this uses a truncated object. :-( :As you can see, you can't get the invalid system stuff in the Ada model. No, this example illustrates something else. :-( :In particular, you should note that the Truck type has _two_ primitive :operations Register_Driver: : : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op The more, the merrier. :WRT b), note that in Ada, you cannot remove operations from derived :types - they can always be used in any context where a parent can be :used. To achieve this sort of effect, you would need to define Truck :differently: : :package Vehicles.Trucks is : : type Truck is tagged private; -- Interface indicates new type : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); :.... :end Vehicles.Trucks; which means you are prevented from reusing the Vehicle abstraction even though it may have a lot of other stuff that is useful and no problem to you. :Lastly, if you really wanted to do an a) like thing, a _client_ could :define the exact semantics of it so that it "works" (at least to the :extent that the client i) knowingly forced the issue, ii) had to :supply semantics that did not violate the rules, and iii) knows what :he's doing...) For example, The purpose of system validity checking is precisely to ensure that the client *does* supply the correct semantics (ie. the correct parameters in the case of a)). The idea is to stop them *before runtime* from doing the wrong thing when they *don't* know what they are doing. The difference in Ada is that this responsibility is transferred to runtime, as you show below: :-- in client... :-- : :procedure Register_Driver (V: Vehicle'Class; D: Driver'Class) is :-- :-- Eats anything... :.... :begin :.... : if V in Truck'Class and not D in Truck_Driver'Class then : -- : -- Do something "appropriate"... Hope I know what I'm doing! The only appropriate thing to do here is raise an exception to signal to the client that what they are attempting is not on. It indicates a logic error in the program which, at the very least should be dealt with (and logged) by the client, or better still, corrected by the developer. ... : elsif V in Car'Class and not D in Car_Driver'Class then : -- : -- Similar special sort of stuff... Ditto. : else : -- : Register_Driver(V, Driver(D)); : end if; :.... :end Register_Driver; What you have described here is precisely a *runtime* system validity check. So, yes, you do have the problem of catcalls in Ada. The difference is that they are manifested at runtime. If Eiffel system validity checking does ever get implemented by vendors, by applying reasonable constraints, then it will be preferable, IMO to what Ada offers because it is better to resolve bugs as early as possible. In the meantime, Eiffel developers must include runtime checks like Ada developers if they want to keep out of mischief. But they still have the added flexibility of more permissive rules WRT reusing existing software. :> :> for runtime. (Yes, I know that few, if any, Eiffel vendors implement :> :> it). Don't know what you mean here. :> : :> :I mean simply that invalid Eiffel systems are knowingly accepted by :> :these implementations. :> :> Correct. : :Note that this does _not_ happen in the Ada case and you loose no :"flexibility" or what-have-you. In fact the Ada model seems to be :significantly more flexible (this is not surprising as you have more :semantic information to work with!) As far as I can see, the ability or otherwise to make a distinction between classwide and specific types is not relevant to this issue. [...] :> No. This is not why. It's due to the distinction between static and dynamic :> types. : :Hmmm, Meyer seems to disagree: : :ETL 22.4 (System Level Validity) : :"...but with generic derivation, expansion and anchored types we will : need to reintroduce the distinction between types and classes." I already corrected myself in an earlier post. You may have missed it. [stuff about the distinction between dynamic class set and dynamic type set] I think ETL is clear enough on the distinction between dynamic class set and dynamic type set and I don't see that it is relevant to whether or not catcalls are an issue in Ada. End of post :-) :-) :-) : : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=-