New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimizing reified types #485
Comments
Didn't @FroMage already have a pretty well thought-out design? Or do you have some issues with it? |
Eh? A well-thought-out design of what? |
First, Second, I'm not really sure what your optimization is doing. Third, I still haven't been given the various use cases y'all have in mind for reification, so it's hard to offer suggestions if I don't know why you need it. |
A question I was wondering about is symmetry of reified types and equality. Suppose the following: class Foo<out T>(T... values){
shared actual Boolean equals(Object other){
if(is Foo<T> other){
return values == other.values;
}
return false;
}
}
class Top(){}
class Bottom() extends Top(){}
value b = Bottom();
value foo1 = Foo<Top>(b);
value foo2 = Foo<Bottom>(b);
assert(foo1 == foo2); // true, because foo2 of type Foo<Bottom> is also a Foo<Top>
assert(foo2 == foo1); // false, because foo1 of type Foo<Top> is not a Foo<Bottom> Am I missing something here or is that going to be problematic? |
@FroMage Right. Note that my implementations of |
OK. |
Further on the idea of "optimized" reification. A critical issue we need to think through is whether there are soundness issues associated with letting a runtime type argument be a subtype the type argument you would get from purely static analysis. The cases I'm thinking of involve contravariance. Imagine we tried to optimize away
Here, if
@RossTate WDYT? |
By the way, the more I think on this issue, the less cases I can think of where reification is really likely to have a very negative impact on performance.
Indeed the main case I'm concerned about (except for the abovementioned Finally, how do reified type impact comprehensions? Anything to worry about there? |
Gavin, your example is not unsound. You're confusing static types and exact types. Reification does not mean these all line up. Reification just means you have a way of determining the type arguments used to allocate an instance of a generic class (and the derived type arguments for inherited classes and interfaces) and the type arguments used to invoke a generic method. From what I understand, there are two main expenses for reification: the space needed per instance of a generic class to store the type arguments (only a problem for certain backends such as Java and JS but not C#) and the allocation done at run time to build the data structures representing type arguments (problematic for all backends). Regarding your concerns with The only concern I can see for comprehensions is that they use inference and we need to make sure inference doesn't affect the semantics; an issue we have mentioned elsewhere. |
Ross, it's unsound if I do the proposed optimization on it.
Nope, it's more the optimization that "confuses" them ;-) |
Sorry, I misunderstood what you were saying and consequently what you were misunderstanding, heheh. Here's my new take. Note that the following still type checks:
Since |
@RossTate In the following code:
The inferred type of
I'm only bringing this up to demonstrate that the type arg inference algorithm does the right thing here, because I do not want this thread to go off on a tangent about type arg inference and contravariant types. In M5 the algorithm does the right thing and I'm completely satisfied with it. All that has nothing to do with what we're discussing here, which is a different example:
Here, static analysis infers the type So obviously we wouldn't do this optimization in this case., because it would result in unsoundness. What we need to understand is in which cases is it ok to optimize away the static argument to a type parameter, and infer it at runtime from the runtime type of an argument? For example, I believe it is sound to do it for tuples, that is, if I have the following:
Then the static type of But I want to go further than that and do the same sort of optimization here, for example:
I want to optimize away
I will get a It's only OK to do this because the function |
After chatting with Ross, instead of talking past each other, as above, it's now clear what the cause of the unsoundness is. Ross points out that we should not:
at least not at runtime, since that is the cause of the unsoundness above. Arguably - indeed, Ross argues this, though I'm not completely sold - we should not even do that at compile time. Anyway, this completely explains what's going on here, and gives us what we need to be able to solve the problem. |
We don't, because we optimise the type descriptor for Ceylon non-parameterised types into |
@FroMage Right, of course. |
Let's delay full consideration of this issue until 1.1, which is when we're going to focus on performance. |
There are three big problems to overcome in our implementation of fully-reified types:
I think we're going to need some way to address this via either optimized or partial reification of type arguments. Let me sketch out the options here:
Partial reification:
We could annotate a type parameter as
static
like this:Then there would be limitations on what you can do with
T
. For example, you would not be able to use it withis
, like this:Nor would you be allowed to pass it as an argument to a reified type parameter:
Of course, all type parameters of Java classes would be considered
static
.The problem with this approach is that I think it would just get in the way much too much. For example, if tuple types were unreified, you would not be able to put instances of parameterized Java classes in them. It's hard to be able to guess in advance just how much pain this approach would cause, because we simply don't have enough experience with this stuff.
"Optimized" reification
In this approach, we let you provide an optimized implementation of the operation which obtains the reified type of an instance.
What we would probably do is say that for every subclass of Object which does not define an attribute named
type
, an attribute with the following signature is generated for it:class C()
, the attributeshared actual Class<C> type
class C<T>()
, the attributeshared actual Class<C<T>> type
class C<U,V>()
, the attributeshared actual Class<C<U,V>> type
The implementation of the generated attribute would include generated fields holding the reified type arguments.
OTOH, a class would be free to define its own
type
attribute. For example:Thus, the instance is responsible for computing its own reified type, and is free to make use of its internal state to do that.
Unfortunately this solution doesn't address the issue of Java classes, which don't have
getType()
methods. I guess we would be left with some nasty runtime exception or some shit.The text was updated successfully, but these errors were encountered: