D - type conformance again: array is just template
- nobody yahoo.com (21/21) Sep 25 2003 I'd like to add more comments regarding D 0.72/0.37 release about the im...
- Philippe Mori (15/36) Sep 25 2003 in
- Daniel Yokomiso (98/119) Sep 25 2003 implicit
I'd like to add more comments regarding D 0.72/0.37 release about the implicit conversions of B[] to A[] if B is derived from A: Array is just a template class known to the compiler (with special syntax []). If I write it verbosely, A[] == Array<A>. Now, supposing I have class Bat : Animal {} // Bat is-a Animal class SortedArray<T> : Array<T> {} // SortedArray is-a Array Array<Animal> animals; SortedArray<Bat > sortedBats; void feed(Array<Animal> as); animals = sortedBats; // should I allowed to do this assignment? feed(sortedBats); // should I allowed to pass the arg? I haven't checked D's template, but I know this kind of thing won't work in C++'s template. But Walter, do you agree these should be allowed? More generally, have you designed some conformance rule for D's template (not just A[] <- B[] as in 0.72 and 0.73)? Simply put, the conformace rule of Eiffel which I send before is defined recursively on each of the type parameter. So you can even have: SortedArray<SortedArray<Bat>> comforms to: Array<Array<Animal>> and Tuple<Bat, Cat, Dog> conforms to: Tuple<Animal, Animal, Animal>.
Sep 25 2003
I'd like to add more comments regarding D 0.72/0.37 release about theimplicitconversions of B[] to A[] if B is derived from A: Array is just a template class known to the compiler (with special syntax[]).If I write it verbosely, A[] == Array<A>. Now, supposing I have class Bat : Animal {} // Bat is-a Animal class SortedArray<T> : Array<T> {} // SortedArray is-a Array Array<Animal> animals; SortedArray<Bat > sortedBats; void feed(Array<Animal> as); animals = sortedBats; // should I allowed to do this assignment? feed(sortedBats); // should I allowed to pass the arg? I haven't checked D's template, but I know this kind of thing won't workinC++'s template. But Walter, do you agree these should be allowed? More generally, have you designed some conformance rule for D's template(notjust A[] <- B[] as in 0.72 and 0.73)? Simply put, the conformace rule of Eiffel which I send before is defined recursively on each of the type parameter. So you can even have: SortedArray<SortedArray<Bat>> comforms to: Array<Array<Animal>> and Tuple<Bat, Cat, Dog> conforms to: Tuple<Animal, Animal, Animal>.I like the idea but to make this works without useless or unintended copying, we need to be able to control when conversion is allowed and when copying is done. Maybe we should have a qualifier that enable conformance for in parameters. In others case (inout and out), I think we should not accept it... If we need it, we should define a conversion function for it. void f(in conformant Array<Animal>) { } SortedArray<Bat> a; f(a);
Sep 25 2003
<nobody yahoo.com> escreveu na mensagem news:bkvfs7$1kg6$1 digitaldaemon.com...I'd like to add more comments regarding D 0.72/0.37 release about theimplicitconversions of B[] to A[] if B is derived from A: Array is just a template class known to the compiler (with special syntax[]).If I write it verbosely, A[] == Array<A>. Now, supposing I have class Bat : Animal {} // Bat is-a Animal class SortedArray<T> : Array<T> {} // SortedArray is-a Array Array<Animal> animals; SortedArray<Bat > sortedBats; void feed(Array<Animal> as); animals = sortedBats; // should I allowed to do this assignment? feed(sortedBats); // should I allowed to pass the arg? I haven't checked D's template, but I know this kind of thing won't workinC++'s template. But Walter, do you agree these should be allowed? More generally, have you designed some conformance rule for D's template(notjust A[] <- B[] as in 0.72 and 0.73)? Simply put, the conformace rule of Eiffel which I send before is defined recursively on each of the type parameter. So you can even have: SortedArray<SortedArray<Bat>> comforms to: Array<Array<Animal>> and Tuple<Bat, Cat, Dog> conforms to: Tuple<Animal, Animal, Animal>.Hi, These issues aren't simple. I'll repost something that I sent to the D newsgroup less than two weeks ago, regarding the conformance rule of "if A <: B then A[] <: B[]" where "<:" means subtype of. ---------------------------------------------------------------------------- - IME this is a Bad Thing. Usually we have three kinds of subtyping: 1. common covariant subtyping: used for values (e.g. integer is a subtype of real); 2. contravariant subtyping: used for functions, where (using "->" to denote function type, "int -> bool" is a function that take an "int" and returns a "bool", and "<:" to denote subtype of) it holds "A' <: A", "B' <: B" and "(A -> B') <: (A' -> B)". That in the subtype the parameter can be generalized and the return type can be constrained. A example would be the the type "real -> int" that is a subtype of "int -> real". A example in D: class Real {...} class Integer : Real {...} Integer i = new Integer(42); Real r = null; Real function(Integer) f; Integer function(Real) g = Integer function(Real r) { return r.truncate(); }; f = g; // contravariant rule say it's ok. r = f(i); // g expects any Real, so it's ok to pass an Integer. // g returns a Integer, so it's ok to treat it as a Real. 3. invariant subtyping: used for mutable arrays. In this situation there's no subtyping possibility, because of possible undesirable assignments: class A {} class B : A {} class C : A {} void bind(A[] as, A a) { as[0] = a; // ok } int main() { static B[] bs = [new B(), new B()]; bind(bs, new C()); // ops, if covariance was ok this would break type-safety. } Java has this hole in their type-system, but they use a runtime check to verify that nobody calls "bind" passing a value that the array rejects. As we have templates in D, which are capable of F-bounded polymorphism (impressive, huh? ;), someone can rewrite bind as: template TBind(T : A) { void bind(T[] ts, T t) { ts[0] = t; } } And the template instatiation would point the error at compile time. IMO it's completely unnecessary to allow array subtyping in a system that has the correct mechanism to write generic array operations. Java doesn't have (1.5 is in the future) generics so they had to allow such holes in their type-system, or else people would have to write the same array libraries for every possible type. It doesn't kill people to write templates (using them may kill some ;) specially when they lead to correct code. If the template syntax starts to get in the way it's a sign that the syntax should be changed. ---------------------------------------------------------------------------- - The important points here are the variance issues. If we define a Function template: public template TFunction(T,U) { public interface Function { public U apply(T value); } } we should have a way to define variance of T (contravariant) and U (covariant). Let's use the symbols "+" (contravariant) and "-" (covariant): public template TFunction(T+, U-) { public interface Function { public U apply(T value); } } Voilá, the compiler is able to verify the typing relations. But an interesting simmetry is perceived. The parameter types must be contravariant, while the return type must be covariant. Try it with other types and you'll see this rule always works. SO probably the symbols "+" and "-" should be just a way to tell the compiler "complain if someone try to use it in the wrong way". BTW the Eiffel type system is unsound because their covariance rule. The interesting thing is that covariance was a feature introduced in Eiffel when Bertrand Meyer wanted to keep F bounded parametric polymorphism out of Eiffel (i.e. before generics). After generics came to Eiffel covariance was just an unnecessary hole in the type system. Best regards, Daniel Yokomiso. "What if nothing matters? What if everything matters? Which would be worse???" --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.521 / Virus Database: 319 - Release Date: 23/9/2003
Sep 25 2003