ceylon / ceylon-spec

DEPRECATED
Apache License 2.0
108 stars 34 forks source link

member resolution for unions of invariant types #904

Open gavinking opened 10 years ago

gavinking commented 10 years ago

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.

pthariensflame commented 10 years ago

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

gavinking commented 10 years ago

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

RossTate commented 10 years ago

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.

gavinking commented 10 years ago

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

RossTate commented 10 years ago

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.

pthariensflame commented 10 years ago

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

RossTate commented 10 years ago

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.

gavinking commented 9 years ago

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.