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!trane.uninett.no!eunet.no!nuug!EU.net!howland.reston.ans.net!cs.utexas.edu!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: Wed, 4 Jan 1995 19:19:10 GMT Message-ID: <1995Jan4.191910.13968@schbbs.mot.com> References: <1995Jan3.223031@hobbit> Sender: news@schbbs.mot.com (SCHBBS News Account) Nntp-Posting-Host: 129.188.128.126 Lines: 268 Xref: news.daimi.aau.dk comp.object:23402 comp.lang.eiffel:7102 comp.lang.sather:1369 comp.lang.beta:188 comp.lang.modula3:3286 In article <1995Jan3.223031@hobbit> writes: > It appears Mr. Shang has added comp.lang.beta and some other(s) to the > distribution list. Perhaps it will expand the conversation to others who > can enlighten us all. I think that the BETA and the Modula3 groups are intersted in this subject too. > > > 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. > > If grass and corn are all they will eat then the way I wrote it is correct. > If they will eat other plants then the constraint that the hog and cow > should only eat grass and corn aren't defined in those classes then it would > have to be introduced later. I wouldn't object to that. We wouldn't want > them eating anything dangerous. :-) As I said already in my previous posts on the subject of covariance, we cannot have covariance for a subclass if we cannot find a general rule for that subclass. If a subclass of herbivore eat all plant (of cause not including poisonous plant, which is not a food), you can have: AllPlantEatingHerbivore is Herbivore [ FoodType=plant]; If a subclass of herbivore eat all most all plants except for a few plants that you cannot rule out a classification on the food they do not eat, you can have: AlmostAllPlantEatingHerbivore is Herbivore [ FoodType=plant]; and use run-time type check to reject those food they do not eat. If a subclass of herbivore eat only a few plants that you cannot rule out a classification on the food they eat, you can also have: SomePlantEatingHerbivore is Herbivore [ FoodType=plant]; and use run-time type check to accept those food they eat. Sometimes multiple inheritance may help the last case to get rid of run-time type check. class Panda is Herbivore [FoodType = BambooLeaf], Herbivore [FoodType = Corn] procedure Herbivore[BambooLeaf]::eat (food: BambooLeaf); procedure Herbivore[Corn]::eat (food: Corn); end Panda; In reality, we always have exceptions. We can use covariance to rule out a general principle and use run-time type check for a few exceptions. As to the animal example, it is clearly worthwhile to divide animals into herbivires, carnivores and (?)(animals eating both food) according to the type of their food. We can use covaraince. But it might be very hard to rule out a general principle for the futher classification for a certain number of animals. > > Then, you can have cows and hogs: > > > > class hog is herbivore [FoodType = corn]; > > class cow is herbivore [FoodType = grass]; > > Funny how you, too, specify corn & grass as the foods these animals can eat. > It's the most natural way to describe a HOG or COW. If there are other > plants these animals eat then there should be a subset of "plants" created > for each of them. We are not developing a commercial software for zoologists, are we? These are only padagogical examples. Be wise to make things as simple as possible -- just for the key points you want to say. > > > 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. > > In the example from the comp.lang.eiffel post there were only HOG & COW and > an example FARM. I tried not to stray too far from that. > > > Firstly, you could have a homogeneous farm where only on type of herbivores > > are raised: > > > > class field [FoodType <: plant] > > function getCrop(): FoodType; > > end field; > > Isn't a generic field which can grow any plant more correct? Doesn't the > notation you've given tie one specific plant to the field and not allow for > future crop rotations? Farmers plant different crops to avoid depleting the > soil too much. This is an abstract field. No object can be intantiated by the class unless you associate it with a plant type. > > > 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]; > > Why would a farm be a herbivore? Sorry, a typo. Should be: class hog_farm is farm [AnimalType = hog]; class cow_farm is farm [AnimalType = cow]; > > I don't care for your syntax which appears to only allow single inheritance. > What if a COW is not only a HERBIVORE, but a DOMESTICATED_ANIMAL? I could > write... > > class cow is > inherit herbivore; > inherit domesticated_animal; > end cow; > Multiple inheritance is fine: class cow is herbivore [FoodType = Grass], domesticated_animal [ FoodType = ManMadeCowFood ]; You can even have: class cow is herbivore [FoodType = Grass], herbivore [FoodType = Hay], herbivore [FoodType = Corn], ... domesticated_animal [ FoodType = ManMadeCowFood ]; Note that herbivore[Grass], herbivore[Hay], ... are anonymous subclasses of herbivore and they share the common base of herbivore. > Perhaps you're right that I should have developed a generic farm and then > populated it with particular animals. That wouldn't be too difficult, as > you showed. Again, I wasn't focusing on these peripheral classes as much > as on the COW and HOG classes and how to build the FARM from them. Note that the generic classes I presented above are not conventional generic classes. You cannot have this by Eiffel's generic class, which is not a real class, and hence it is meaningless to discuss the subclass relationship between a generic class and the class instantiated by the generic class. > > > 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. > > What if the farmer sells some cows and buys some hogs to have a mixed farm? > How do you readjust to handle this? Being owners of homogeneous farms, they cannot have a mixed farm. Being owners of homogeneous farms, farmers can sell their creatures to a market that accept the type of their creatures: cow_farm sells cows to cow market -- correct cow_farm sells cows to hog market -- incorrect cow_farm sells cows to mixed market -- correct Being owners of homogeneous farms, farmers can only buy their creatures from a market sells the type of their creatures: cow_farm buys cows from cow market -- correct cow_farm buys cows from hog market -- incorrect cow_farm buys cows from mixed market -- ensured by run-time type check The last case must be ensured by a run-time type check, beacuse it is a reverse assignment: cow_farm::buy(creature: cow); mixed_market::sell():herbivore; aCow_farm.buy(aMixed_market.sell()); -- wrong, reverse assignment here! You should write the code like this: a: herbivore := aMixed_market.sell(); when typeof(a)=cow do aCow_farm.buy(a) otherwise aMixed_market.send_back(a); > > > > 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; > > Why do you say "animals: list [MemberType <: herbivore];" > instead of animals: list [ herbivore ]; > ? > The latter is equivalent to: animals: list [ MemberType = herbivore ]; where "animals" is not a polymorphic (heterogeneous) variable. > And, how does "herbivore" define a COW or HOG when they could easily be > created from both HERBIVORE and DOMESTICATED_ANIMAL? What would you do > in that case? > By multiple inheriatnce as I shown above. > > 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. > > This version of farming() looks pretty good. I like the use of "typeof()" > also. It's quite useful. My example was difficult to read compared to this. > > > 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: > > But, if an animal (or group of animals) is put in a field simply for > temporary > holding then there's no plan for them to eat there and no need to observe > the binding of animal-to-field. That binding only needs to be checked when > they're trying to eat. > There could be a lot of buts. But I don't like to make things unnecessarily complicated for the subject discussed here. David Shang