D - type conformance again: array is just template

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>.

C++'s template.
But Walter, do you agree these should be allowed?
More generally, have you designed some conformance rule for D's template

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>.

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);

C++'s template.
But Walter, do you agree these should be allowed?
More generally, have you designed some conformance rule for D's template

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>.

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