eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
399 stars 62 forks source link

member resolution for unions of invariant types #4010

Open CeylonMigrationBot opened 10 years ago

CeylonMigrationBot commented 10 years ago

[@gavinking] Currently, things as simple as this:

ArrayList<Foo>|ArrayList<Bar> list = ... ;
value size = list.size;

Result in errors because size resolves to ArrayList.size, and there is no principal instantiation of ArrayList. Of course, the typechecker should be smart enough to look at List.size, which is something it will generally in other cases.

[Migrated from ceylon/ceylon-spec#904]

CeylonMigrationBot commented 10 years ago

[@pthariensflame] Of course, things here (and elsewhere) would be much simpler if Ceylon had "bidirectional projections", perhaps with the syntax you gave in the language design FAQ: ArrayList<Foo>|ArrayList<Bar> would be a subtype of ArrayList<out Foo|Bar in Foo&Bar>.

CeylonMigrationBot commented 10 years ago

[@gavinking] @pthariensflame True, that would make this problem go away.

CeylonMigrationBot commented 10 years ago

[@RossTate] The two general solutions I can see for this are either adopting in out or making the type of an attribute of a union of types be the union of the types of the attribute for those types.

CeylonMigrationBot commented 10 years ago

[@gavinking] @RossTate You just look up the type hierarchy until you notice that size is actually refining a declaration of the covariant interface List, which does have the principal instantiation List<Foo|Bar>.

CeylonMigrationBot commented 10 years ago

[@RossTate] Right, but in general that might not be possible. I believe that technique will arrive at the same/equivalent solution as both of mine whenever it applies, but it won't always be able to apply where as mine will.

CeylonMigrationBot commented 10 years ago

[@pthariensflame] Oddly enough, Ceylon('s type system) can already partly support this. The existing system of projections would allow us to simply say that the compiler should treat any read-access of ArrayList<Foo> as a read-access of ArrayList<out Foo>, and it should likewise treat any write access of ArrayList<Foo> as a write-access of ArrayList<in Foo>.

CeylonMigrationBot commented 10 years ago

[@RossTate] You need both in and out in one if you're gonna solve this with use-site variance because an attribute may itself return an invariant usage of the type parameter.

CeylonMigrationBot commented 9 years ago

[@gavinking] FTR, in Ceylon 1.2, I made the code above compile, by forming the supertype instantiation ArrayList<out Foo|Bar>. This isn't of course totally correct because it privileges covariance over contravariance, and thus list.add(fooAndBar) doesn't compile.

I guess I could get even closer to a "correct" solution by inspecting the operation to see if the type parameter occurs covariantly or contravariantly in its signature and using that information to decide whether to use ArrayList<out Foo|Bar> or ArrayList<in Foo&Bar>, though of course that doesn't help much in the less common case where the type parameter occurs both covariantly and contravariantly in the signature of the operation.