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!pipex!howland.reston.ans.net!math.ohio-state.edu!news.acns.nwu.edu!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: Thu, 5 Jan 1995 18:35:34 GMT Message-ID: <1995Jan5.183534.10065@schbbs.mot.com> References: <3e07c4$sup@muss.cis.McMaster.CA> Sender: news@schbbs.mot.com (SCHBBS News Account) Nntp-Posting-Host: 129.188.128.126 Lines: 225 Xref: news.daimi.aau.dk comp.object:23450 comp.lang.eiffel:7107 comp.lang.sather:1380 comp.lang.beta:195 comp.lang.modula3:3291 In article <3e07c4$sup@muss.cis.McMaster.CA> u9442202@muss.cis.McMaster.CA (E.H. Schnetter) writes: > OK, here is an example in Sather. I didn't include much fancy stuff. > type $HERBIVORE is > -- an abstract HERBIVORE > name: STR; > eat (food: $PLANT); > end; -- type $HERBIVORE > > class HERBIVORE is > -- a general herbivore > attr name: STR; > create (name: STR): SAME is res::=new; res.name:=name; return res end; > eat (food: $PLANT) is raise "HERBIVORE::eat not defined\n" end; > end; -- class HERBIVORE > > class COW < $HERBIVORE is > include HERBIVORE; > eat (food: $PLANT) is > typecase food > when GRASS then > #OUT + "Cow " + name + " eats " + food.name + "\n"; > else > raise "Error: Cow " + name + " cannot eat " + food.name + "\n"; > end; -- typecase > end; -- eat > end; -- class COW > > class HOG < $HERBIVORE is > include HERBIVORE; > eat (food: $PLANT) is > typecase food > when CORN then > #OUT + "Hog " + name + " eats " + food.name + "\n"; > else > raise "Error: Hog " + name + " cannot eat " + food.name + "\n"; > end; -- typecase > end; -- eat > end; -- class HOG > > type $PLANT is > -- an abstract plant > name: STR; > end; -- type $PLANT > > class PLANT is > -- a general plant > const name: STR := "general plant"; > create: SAME is return new end; > end; -- class PLANT > > class GRASS < $PLANT is > include PLANT; > const name: STR := "grass"; > end; -- class GRASS > > class CORN < $PLANT is > include PLANT; > const name: STR := "corn"; > end; -- class CORN > > class FARM is > > attr livestock: ARRAY{$HERBIVORE}; -- cows and hogs > attr foodstuff: ARRAY{$PLANT}; -- grass and corn > > main is > -- Set up the farm > livestock := |#COW("Elsa"), #HOG("Peter Pan")|; > foodstuff := |#GRASS, #CORN|; > -- Feed everything to everyone > loop -- all the food > food::=foodstuff.elt!; > loop -- all the animals > protect > -- try to feed... > livestock.elt!.eat(food); > when STR then > -- oops, went wrong > #OUT + exception; > end; -- protect > end; -- loop animals > end; -- loop food > end; -- main > > end; -- class FARM > What you presented here is "Invaraince + Runtime Type Check", not a covariance. At a hogsty, if there is a plate saying "please feed me any plant", what will hapen? The sty will be full of rotten food that the hog does not eat. The plate should say "please feed me corn only". That is, the eat interface should tell its feeder the right food (if we suppose that a hog eat corn only). I'm not syaing that an interface should always tells the exact type. It depends on the desired semantics for that function. "eat" definitely needs an exact interface: hog eats corns only -- covariance "try" may have a vaguer interface: hog try all plant -- invaraince + runtime type check As I always said, a good language should provide all the possiblities. When hog knows "x" is a corn, it should be able to eat "x" without run-time type check: cornstuff: list[corn]; aHog.eat (cornstuff.first); When a herbivore knows "x" is the food that it eats, it should be able to eat "x" without run-time type check: [HerbivoreType <: herbivore] ( aHerbivore: HerbivoreType; food: HerbivoreType.FoodType ) enter aHerbivore.eat(food); end; But by Sather, run-time type check is always a neccessity. And by Eiffel's covariance, a system validation is required for the above case. By Cluster, runtime type check is not necessary for the above cases. It is only necessary when a herbivore cannot identify whether the food is its food. This happens only in the case of a reverse/cross assignment: foodstuff: list[plant]; ... aHog.eat (cornstuff.getfirst()); -- wrong! reverse assignment here -- We should use run time type check here: for each food in foodstuff do { when typeof(food) is corn do { aHog.eat(food); break; } } Run time type check can also be encapsulated by a hog's method, say "try": function hog::try(food: plant): plant enter when typeof(food) is corn do { eat (food); return nil; } return food; end; Back to the original example when both the food type and the animal type are unknown: livestock: list[herbivore]; foodstuff: list[plant]; ... livestock.getfirst().eat (foodstuff.getfirst()); -- wrong! cross assignment here -- we should write code like this: hungry: list[herbivore] = {}; unused: list[plant] = {}; for each animal in livestock do { for each food in foodstuff do { food:= animal.try(food); if food<>nil then { for each animal in hungry do { food:= animal.try(food); if food=nil then { hungry.remove(animal); break; } } if food<>nil then unused.add(food) } else { animal := nil; break; } } if (animal<>nil) then { for each food in unused do { food:= animal.try(food); if food=nil then { unused.remove(food); animal:=nil; break; } } if animal<>nil then hungry.add(animal); } } From the above example, we can see that a runtime type mismaych is not necessarily an exception, nor a type error. The system is designed this way. It is the desired semantics. What is a type error? A type error happens only when the type voilates the requirement of the interface specification. hog eats grass is a type error, but hog tries grass is not a type error. Runtime type check is not an very important issue. The keypoint is that whether the language can say the way you want! David Shang