Path: news.daimi.aau.dk!news.uni-c.dk!sunic!pipex!howland.reston.ans.net!spool.mu.edu!darwin.sura.net!wvnvms!marshall.wvnet.edu!marshall.edu!hathawa2 Newsgroups: comp.object,comp.lang.eiffel,comp.lang.sather,comp.lang.beta,comp.lang.modula3 Subject: Re: Cows, Hogs & Farms Message-ID: <1995Jan3.223031@hobbit> From: hathawa2@marshall.edu (Mark S. Hathaway) Date: 3 Jan 95 22:30:31 EDT References: <1994Dec29.013815@hobbit> <1995Jan3.175336.14940@schbbs.mot.com> Organization: Marshall University Nntp-Posting-Host: frodo.marshall.edu Lines: 304 Xref: news.daimi.aau.dk comp.object:23369 comp.lang.eiffel:7098 comp.lang.sather:1363 comp.lang.beta:185 comp.lang.modula3:3275 > In article <1995Jan3.175336.14940@schbbs.mot.com>, > shang@corp.mot.com (David L. Shang) writes: >> In article <1994Dec29.013815@hobbit> >> hathawa2@marshall.edu (Mark S. Hathaway) 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? >> ... 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. >> 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. I'm not keen on the idea of a class body having global data, so the added parameter binds the method to particular data within the class. There could be several variables which together define the object. By binding only the particular one(s) to a method it should be clearer what is acting on what. Will you elaborate on your view? > 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. :-) I was following the example of the original post to comp.lang.eiffel. > 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; A herbivore's food is plant material. I only gave the type plant as the foods there might be on a farm. If I was being more thorough I'd have defined plant more completely and have made even more readers angry with me for making the post too long. Besides, it wasn't the point of the example. In my example the "way it eats" is undefined. All that is recorded is what was eaten most recently. I didn't even begin to discuss the chewing of cud. Without defining "plant" you've left yourself open for problems. Somehow someone will have to get around to defining it for this to work. > 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. > 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. > 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? 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; 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. All this shows how difficult it is to fully define something as simple as a farm. Once you begin to model the real world things get very complicated. > 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? > 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. Sure, it's easier if it's a homogeneous farm because you don't have to figure-out what food to match with which animal and if it's grown in the field where the animal is and if it's ripe. The real world is complicated. > 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; Why do you say "animals: list [MemberType <: herbivore];" instead of animals: list [ herbivore ]; ? 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? > 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. > 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. I'd like to know why you didn't address the identification of individual animals. For a long time COBOL and FORTRAN were considered "good" languages and they didn't provide facilities to do these class things. I guess we become comfortable with those early developments and begin to take them for granted. Now, a good language is soooo much more. I doubt we're seen a quiescence of language development regarding features which allow us to develop programs using the OO paradigm. > The language BETA and Cluster come close to what I presented above. > > David Shang I've not seen BETA and have never heard of Cluster. Could you provide a pointer-->ftp_site or other info. about them? :-) Finally, I apologize to those who thought my original post was too long at more than 300 lines. I thought it necessary to flesh-out the problem at hand. In fact, as Mr. Shant points out, it really requires more than I gave. Mark S. Hathaway