Newsgroups: comp.lang.eiffel,comp.object,comp.lang.modula3,comp.lang.sather,comp.lang.beta Path: news.daimi.aau.dk!news.uni-c.dk!sunic!news.tele.fi!news.csc.fi!news.eunet.fi!EU.net!uknet!comlab.ox.ac.uk!sable.ox.ac.uk!lady0065 From: lady0065@sable.ox.ac.uk (David J Hopwood) Subject: Re: Cows, Hogs & Farms Message-ID: <1995Jan10.180233.23224@inca.comlab.ox.ac.uk> Sender: david.hopwood@lmh.oxford.ac.uk Organization: Oxford University Computing Service, 13 Banbury Rd, Oxford, UK References: <3e07c4$sup@muss.cis.McMaster.CA> <1995Jan5.183534.10065@schbbs.mot.com> Date: Tue, 10 Jan 95 18:02:33 GMT Lines: 140 Xref: news.daimi.aau.dk comp.lang.eiffel:7140 comp.object:23641 comp.lang.modula3:3320 comp.lang.sather:1406 comp.lang.beta:210 In article <1995Jan5.183534.10065@schbbs.mot.com>, David L. Shang wrote: >In article <3e07c4$sup@muss.cis.McMaster.CA> u9442202@muss.cis.McMaster.CA >(E.H. Schnetter) writes: >> OK, here is an example in Sather. [example using type-cases snipped] >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 >happen? 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. Exactly. In the general case, we have an Animal which is guaranteed to eat all food of type A, and may in addition eat food of type B: type Animal {A, B} eat (A) -- accept _at least all_ food of type A try (partial B) -- accept _at least some_ food of type B A hog is guaranteed to eat all corn, and may in addition eat other plants: type Hog eat (Corn) try (partial Plant) Note that Hog is intended to be equivalent to Animal {Corn, Plant}; they represent the same type. The argument of try is declared to be partial, to indicate that it is not necessarily defined for all Plants. Suppose that a hog, fred, eats both corn and maize, but nothing else (and in particular, not venus-flytraps): venus-flytrap: Plant := ... maize: Plant := ... corn: Corn := ... fred: Hog := ... fred.try (venus-flytrap) -- legal, but fails at run-time fred.try (maize) -- legal, and succeeds at run-time fred.try (corn) -- legal, and succeeds at run-time fred.eat (venus-flytrap) -- illegal (detected statically) fred.eat (maize) -- also illegal, since the semantics of eat requires -- that success is guaranteed fred.eat (corn) -- legal, no run-time check required try and eat have different semantics, in that they make different guarantees about whether the call will succeed. Most languages do not allow try and eat to be declared differently (ie. they have no equivalent to 'partial'). One reason for declaring them differently is as a warning to the programmer. To use try properly, the program must be designed so that it does not matter if it succeeds or not (as in David Shang's excellent example of type-safely matching a heterogeneous list of animals with a list of foods). Another reason is that they behave differently with respect to subtyping. Ordinary parameters are contravariant, but partial parameters are not necessarily so. For example, is Animal {Corn, Maize} a subtype of Animal {Corn, Plant}? It must be, since fred is evidently a member of type Animal {Corn, Maize}, and in the above example we declared fred to be of type Hog (ie. Animal {Corn, Plant}). Ie. the argument of try (in this case) is covariant. Consideration of similar examples shows that a programming language cannot impose _either_ co- or contravariance on the types of partial arguments, without rejecting some perfectly type-safe programs. It must allow these types to be changed arbitrarily. Since the programmer is required to handle the case where the method fails, this does not contravene type safety. [much snipped] >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; try should allow the run-time type of its argument to be a subtype of corn. It should not restrict it to being exactly corn. Ie. the language should allow something like: when typeof(food) is-subtype-of corn do ... >Back to the original example when both the food type and the animal type are >unknown: [example of type-safely matching a heterogeneous list of animals with their corresponding foods snipped] >From the above example, we can see that a runtime type mismatch 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 key point is >whether the language can say the way you want! > >David Shang Agreed. David Hopwood david.hopwood@lmh.oxford.ac.uk