Newsgroups: comp.object,comp.lang.eiffel,comp.lang.sather,comp.lang.beta,comp.lang.modula3 Path: news.daimi.aau.dk!news.uni-c.dk!sunic!uunet!ftpbox!mothost!schbbs!news From: shang@corp.mot.com (David L. Shang) Subject: Re: Cows, Hogs & Farms Reply-To: shang@corp.mot.com Organization: MOTOROLA Date: Tue, 3 Jan 1995 17:53:36 GMT Message-ID: <1995Jan3.175336.14940@schbbs.mot.com> References: <1994Dec29.013815@hobbit> Sender: news@schbbs.mot.com (SCHBBS News Account) Nntp-Posting-Host: 129.188.128.126 Lines: 197 Xref: news.daimi.aau.dk comp.object:23335 comp.lang.eiffel:7095 comp.lang.sather:1359 comp.lang.beta:180 comp.lang.modula3:3272 In article <1994Dec29.013815@hobbit> writes: > Note: This post was originally on comp.lang.eiffel > > > In article > > milod@netcom.com (John DiCamillo) writes: > > >> chrisb@stork.cssc-syd.tansu.com.au (Chris Bitmead) writes: > > >>> In article > >>> milod@netcom.com (John DiCamillo) writes: > > >>> How does covarience *really* work in Eiffel? > >>> > >>> class COW > >>> inherit > >>> HERBIVORE redefine eat > >>> feature eat(food: GRASS) is ... > >>> > >>> class HOG > >>> inherit > >>> HERBIVORE redefine eat > >>> feature eat(food: CORN) is ... > >>> > >>> class FARM > >>> feature > >>> LIST[HERBIVORE] livestock; -- cows and hogs > >>> LIST[PLANT] foodstuff; -- grass and corn > >>>... > >>> livestock.value.eat(foodstuff.value); > >>> > >>> Is there a compile-time or a run-time error? What > >>> happens? > > > I'm cross-posting this to comp.object, comp.lang.eiffel & comp.lang.sather > to get some discussion from people who are trying to do these complex > things as well as for those of us who are still just talking about it. > > Here's how it might be done: > ... > class herbivore is > inherit plants.plant; > procedure eat ( food is in plant ); > end herbivore; > > class herbivore body is > variable data is plant; > procedure eat ( get data is out plant; -- "get" binds data to the proc. > food is in plant ) is > begin > data := food; -- food the herbivore has most recently eaten > end eat; > end herbivore; > ... To make the implementation interface diferent from the exported interface is not a good idea to handle this situation. By your herbivore class, you declare that a herbivore can eat any plant. Therefore, if hog and cow are herbivores, they should eat any plant, otherwise they are not herbivores. If hog and cows are herbivores in the real world, then, your definition of herbivore class is wrong. It makes a false promise on which its subclasses cannot agree. It really depends on the precision that you want to model the real world. If you do not care the futher classification of herbivores on their food types (plants), the definition of "procedure eat ( food is in plant )" is fine for a herbivore class. But if you do care the classification of herbivores on their food types, herbivore should be an abstract class because its real food type is unknown yet. The way it eats may be unknown either. Therefore, a herbivore class should be: class herbivore [FoodType <: plant] is procedure eat ( food is in FoodType ); end herbivore; Then, you can have cows and hogs: class hog is herbivore [FoodType = corn]; class cow is herbivore [FoodType = grass]; If farms are a collection of herbivores with a field where their food grows, we should think about how to model a farm. There are three different ways. Firstly, you could have a homogeneous farm where only on type of herbivores are raised: class field [FoodType <: plant] function getCrop(): FoodType; end field; class farm [AnimalType <: herbivore] is animals: list [MemberType = AnimalType]; fields: list [MemberType = field [FoodType = AnimalType.FoodType]]; function getRipeFood(): AnimalType.FoodType; -- get the ripe food from one of its fields. ... end farm; class hog_farm is herbivore [AnimalType = hog]; class cow_farm is herbivore [AnimalType = cow]; You do not need any run-time type check or system validation for this kind of farm within a close world: program farming is c is cow_farm; h is hog_farm; begin ... for each animal in c.animals do animal.eat(aCorn); -- wrong! for each animal in c.animals do animal.eat(aGrass); -- correct. for each animal in c.animals do animal.eat (c.getRipeFood ()); -- correct. end farming. You do not need any run-time type check or system validation for this kind of farm even with an open world if the type dependency is known statically: procedure farming [FarmType <:farm] (f: FarmType; food: FarmType.AnimalType.FoodType) begin ... for each animal in f.animals do animal.eat(food); -- correct end farming. procedure self_farming (f: farm) begin ... for each animal in f.animals do animal.eat(f. getRipeFood()); -- correct end self_farming. Secondly, you could have an ill-structured heterogeneous farm where various type of herbivores are raised at random fields. That is, there is no relationship between a field type and a herbivore type: class farm is animals: list [MemberType <: herbivore]; fields: list [MemberType <: field]; function getRipeFood(): plant; -- get the ripe food from one of its fields. end farm; You need run-time type check or system validation for this ill-structured farm: procedure self_farming is (f is in farm) begin ... for each animal in f.animals do animal.eat(f.getRipeFood ()); -- wrong! for each animal in f.animals do plant food := f.getRipeFood(); when typeof(animal).Foodtype = typeof(food) do animal.eat(food); -- correct end end self_farming. Lastly, you could have an well-structured heterogeneous farm where each type of herbivores are raised at the field that grows their own food. That is, there is a relationship between a field type and a herbivore type: class group [AnimalType <: herbivore] is animals: list [MemberType = AnimalType]; fields: list [MemberType = field [FoodType = AnimalType.FoodType]]; function getRipeFood(): AnimalType.FoodType; -- get the ripe food from one of its fields. ... end group; class farm is groups: group; ... end farm; When you do self farming, you do not need run-time type check or system validation for this well-structured farm: procedure self_farming is (f is in farm) begin ... for each group in f.groups do for each animal in group do animal.eat(group.getRipeFood ()); -- correct! end self_farming. I really cannot tell you which way is the best. It depends on the requirement of your problem domain. However, a good language should provide all the possible ways. The language BETA and Cluster come close to what I presented above. David Shang