Re: Covariant Return Types
Arjan Boeijink <[email protected]> Tue, 24 Feb 2004 15:43:16 +0100
| Newsgroups | gmane.comp.lang.nice.general |
|---|---|
| Message-ID | <[email protected]> |
>I am curious about the meanings of "method," "method implementation,"
>"specializes," "overrides," and "overloads."
>
>Here is a simple example of my confusion of overriding vs. overloading
>("overrides" syntax made up):
>
> package a;
> public class A { }
> public class B extends A { }
> public A self(A a) { log("A -> A"); return a; }
> B self(B b) overrides A self(A a) { log("B -> B"); return b; }
>
>The intention is that:
> A aa = new A ();
> B bb = new B ();
> A ab = new B ();
>
> A aaa = aa.self(); // prints "A -> A";
> A abb = bb.self(); // prints "B -> B";
> A aab = ab.self(); // prints "B -> B";
> B bbb = bb.self(); // prints "B -> B";
>
>Now, how many =self= methods do we have? One method with two implementations? Or
>two methods, with the first one having two implementations and the second one
>having one implementation.
From typechecking / resolving point of view there are 2 methods but for dispatching it's one method.
> If you say there is one method with two
>implementations, then the type of that method is complex:
> self : (A -> A) or (B -> B).
I guess this would be a mess for typechecking.
>If you say that there are two methods, then they would be typed:
> self(A) : A -> A
> self(B) : B -> B
>In this case, the second definition of =self= is simultaneously overriding and
>overloading the first one. I believe that this second way of thinking is close
>to the Java 1.5 model. If the second model is chosen, then is the following code
>equivalent?:
> package a;
> public class A { }
> public class B extends A { }
> public A self(A a);
> B self(B b) overrides A self(A a);
> self(A a) { log("A -> A"); return a; }
> self(B b) { log("B -> B"); return b; }
public A self(A a) { return a; } is just syntactic sugar for public A self(A a); self(A a) { return a; }
So this code is just a desugared version of the code above.
>If so, it isn't much different than:
> package a;
> public class A { }
> public class B extends A { }
> public A self(A a);
> B anothername(B b) overrides A self(A a);
> self(A a) { log("A -> A"); return a; }
> anothername(B b) { log("B -> B"); return b; }
>That is, allowing specialization of a method by another method that doesn't even
>have the same name.
This would be doable but it will yield new problems for example adding an implementation like:
self(B a) { return new A(); }
>Now, consider something like this (current Nice syntax):
> package b;
> public class A { }
> public class B extends A { }
> public A self(A a) { log("A -> A"); return a; }
> self(B b) { log("B -> B"); return b; }
>Now, we want to say that there is one method that has two implementations,
>right? That is, we don't want say that =self= is overloaded in this case, right?
Yes.
>Do we want clients of this package to know that the second implementation even
>exists?
If the client can add implementations for that method it needs to know which implementations exist.
When adding self(B a) { ... } in the client package the compiler will give an ambiguous implementation error at coverage testing.
> In other words, do the specializations of a public method become part of
>the public interface of the package, or is it enough for clients to know that
>there is a method =self= with type A -> A? Is the above going to be equivalent to:
> package b;
> public class A { }
> public class B extends A { }
> public A self(A a) { log("A -> A"); return a; }
> // explicitly noted to return A
> A self(B b) { log("B -> B"); return b; }
>or, is =self= now considered to be overloaded again?
At the moment this is considered overloading. But that should probably change because almost everyone is used to writing return types when overriding some methods. And if the implementation is in a different source file as the definition allowing a return type when overriding a method can improve the readability of the code.
>It seems perhaps that there may need to be a distinction between
>"implementation" and "specialization". Specialization is a relation between
>methods (possibly with identical names, possibly with different names).
>Implementation is the relation between a particular method M and a set of method
>implementations. That set of implementations is the union of all implementations
>defined directly for M and those defined for all specializations of M
>(transitively).
Agreed.
>> > As an alternative, imagine a "method" keyword that must
>> > be prefixed to a method declaration. Then you have:
>>
>> Yes, that's true: instead of marking overriding implementations, you can
>> mark declarations. It works, the point being that implementation and
>> declaration should look different.
>
>I guess this doesn't work in cases where the method we want to override has
>already been overloaded:
>
> package c;
> class A { }
> A doSomething(A a) { ... }
>
> package d;
> import c;
> void doSomethingElse(A a) { ... }
>
> package e;
> import c;
> void doSomethingElse(A a) { ... }
>
> package f;
> import c;
> import b;
> import c;
> class B extends A { }
> B doSomething(B a) { ... }
> doSomethingElse(B b) { ... }
>
>We should be able to say that f.doSomething(B) is NOT a specialization of
>c.doSomething(A). If you require specializations to be explicit then there is no
>issue for that.
I think specializations should be explicit so this case would give an compile error because you're specializing on the return type.
> Similarly, we should be able to say whether f.doSomethingElse(B)
>is a specialization of d.doSomethingElse(a) or e.doSomethingElse(A).
Yes, maybe using the fully qualified method name for that like:
d.doSomeThingElse(B b) { ... }
> But, should we be able to say that f.doSomethingElse(B) is a specialization of _both_?:
> doSomethingElse(B b) overrides d.doSomethingElse(A),
> e.doSomethingElse(A) { ... }
Maybe, but first we need to look at how useful this is in practice. And what about the reversed case, where a new definition is a generalization of one or more methods?
>It seems that there is a big syntactical burden (lots of keyboarding) to
>requiring overriding to be explicit:
[snipped code showing explicit overriding is a bad idea]
I think the best solution is making method definitions explicit (using a "method" prefix?) so that return type can be allowed at method overrides. And specialization should be explicit too.
>
>Another case is specialization by value:
> package r; // must be getting close to the end!!!
> public class A { }
> public class B { }
> public <T> T fromString(String s) { throw new Error(); }
> A fromString("a") = new A();
> B fromString("b") = new B();
>Do you want to allow this? It would make the type of =fromString=:
> <T> String -> T or "a" -> A or "b" -> B
>This seems to make the type system very complicated.
I think this is not useful enough to justify making the type system more complex.
Arjan
-------------------------------------------------------
SF.Net is sponsored by: Speed Start Your Linux Apps Now.
Build and deploy apps & Web services for Linux with
a free DVD software kit from IBM. Click Now!
http://ads.osdn.com/?ad_id=1356&alloc_id=3438&op=click