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: fac41,c52c30d32b866eae X-Google-Attributes: gidfac41,public X-Google-Thread: 103376,2ea02452876a15e1 X-Google-Attributes: gid103376,public X-Google-Thread: 1108a1,c52c30d32b866eae X-Google-Attributes: gid1108a1,public From: jsa@organon.com (Jon S Anthony) Subject: Re: Real OO Date: 1996/05/02 Message-ID: X-Deja-AN: 152650055 sender: news@organon.com (news) references: organization: Organon Motives, Inc. newsgroups: comp.lang.eiffel,comp.lang.ada,comp.object Date: 1996-05-02T00:00:00+00:00 List-Id: In article donh@syd.csa.com.au (Don Harrison) writes: > :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. No. What I was refering to was that vehicles in general and cars and trucks in particular don't register drivers of any sort. > Your later example is closer semantically to my Eiffel example, but a few > comments are in order: Closer than what? >[snipped example] > : -- 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? The tags of D and V (the dynamic types) do not get changed, only the corresponding values of the approppriate components get copied. The rules for this sort of thing are simply: 1. The tag (dynamic type) of an object never changes 2. Conversion is never away from the root So, there is no problem as nothing gets "truncated" and the operation is _statically_ bound to the exact operation. > 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. Both are illegal: You can't have uniniitialized classwide objects (V needs a legal initialization). And the second is both an illegal conversion (Truck(V)) and assignment. What you are about is an extension aggregate. For example, T := (V with ) > Neither is possible in Eiffel. And neither is possible in Ada. This sort of thing is too obvious to miss in any language design. > : -- 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. :-( Huh?? Where?? No truncated objects around. Why do you think this???? HOW can you think this?? :-|. Note that a classwide "conversion" here (actually any conversion in this context) is a _view_ conversion. The entire object is completely unchanged. In the case above all that actually happens is that the calls are dispatched at runtime based on the dynamic type (tag) of the objects involved (V and T) and the chosen operation "gets" the complete object. Since you clearly do not understand what is happening here, why do you make a _pronouncement_ about it?!? One that is just plain _false_????? > :As you can see, you can't get the invalid system stuff in the Ada model. > > No, this example illustrates something else. :-( Yes, it illustrates that you don't know what you're talking about, but proceed to make pronouncements anyway... :-( :-( :-( > :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. AGGHHHHHHHHHH!!!!!! Don, it is this fact that prevents the system validity problems. You just are not getting what is going on here. It is important to realize that because the types Driver and Truck_Driver are specific types that there are indeed TWO operations. You could override the one with the Driver signature, but by writing the other one with the _new_ specific type you create a new primitive operation. This then can be used to prevent the system validity problems that Eiffel has. > :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. Of course you are not prevented from this!!! Here is where you use the difference between interface and implementation "inheritance" because you have the split between _specification_ and _implementation_! ... Trucks as above... private type truck_impl is new vehicle with type Truck is tagged record impl: truck_impl; end record; end Vehicles.Trucks; Now you simply have the operations dispatch on the implementation. Cake. And you don't violate any "substitution" principle. Really this is more of a win-win, where as the Eiffel is sort of a lose-lose. > :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: I am about to give up. What I show is how you _can_ forceably break things in a controlled way that are BROKEN FOR FREE in Eiffel. Why? Because this may well be a necessary evil under some circumstance and that in doing so you have to go _way_ out of your way to do it. Typically all the system validity problems of Eiffel are handled at _compile_ time in Ada as the above showed. You are just plain complete confused and in the weeds here. So far in the weeds it appears that no one can actually communicate with you... > :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. How do _you_ know what's appropriate here???? That's the point!! This is a case where the typical "correct" thing is WRONG!! The typical correct thing is handled at compile time in Ada. There may be all sorts of "reasonable" things to do! Log an error and then regroup by dispatching on the _perfectly valid and legal_ inherited Truck,Driver operation. > ... > : elsif V in Car'Class and not D in Car_Driver'Class then > : -- > : -- Similar special sort of stuff... > > Ditto. Ditto is right, .... > : 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 >lots of incorrect stuff blah blah blah... No. This example is _beyond_ system validity. There is no validity problem as such. You are just lost. You could attempt to do something like this in Eiffel (though it would be much uglier and convoluted) by using assignment attempt. > 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. BUT Ada ALREADY DOES THIS!!!!! AIIIEEEEEEE!!!! COP A CLUE!!!!!! > 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. HOW???? You still haven't given even the slightest indication of how. THIS IS LUDICROUS! > :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. Yes, as far as you can see, but that's because you are IN THE WEEDS! > 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. They are not an issue in Ada. They are not even relevant. But the dynamic class/type sets are pretty much _implicit_ things corresponding to classwide types. You really had me pulling my hair out on this one... /Jon -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com