On Nov 3, 2021, at 6:19 PM, Kevin Bourrillion 
<kev...@google.com<mailto:kev...@google.com>> wrote:

I think my intuitions about boxes tie heavily to 'getClass' behavior (or some 
analogous reflective operation). "What are you?" should give me different 
answers for a bare value and a box. A duck in a box is not the same thing as a 
duck.

The analogy here would be that Integer.getClass() returns Integer.class, while 
int.getClass(), if it existed, would return int.class.

So far so good. If `int.getClass()` has to work at all, it might as well 
produce `int.class`, though it serves no actual purpose and we would just 
refactor it to `int.class` anyway. If `int.getClass()` won't even compile, it 
would be no great loss at all. The method exists for finding the dynamic type 
of an object; my model says "values are not objects and so have no dynamic 
type", which I think is good.

But Point extends Object, and Object.getClass exists.

One thing the user model has to explain is how method inheritance works. You've 
been pointing out that inheritance != subtyping, which is true. But still, when 
I invoke a super method (a default method in a superinterface, say), it must be 
true that that method declaration knows how to execute on a value.

The ref/val model explains this by saying that method invocation will 
add/remove references to align with the expecations of the 
(dynamically-selected) method implementation. The object remains the same, so 
'this' is the object that the caller started with.

I guess the value/object model would pretty much say the same thing, except it 
would say the value the caller started with might be boxed (or the object 
unboxed) to match the method's expectations. It's the same *value*, presented 
as an object.

Either way, if I can invoke 'getClass', its behavior is specified by the 
*class* not the value/object, so I would expect to get the same answer whether 
invoked via a value or a reference/box.

(Another thing you could say is that the super method is like a template, 
stamped out in specialized form for each primitive subclass as part of 
inheritance. We experimented with this way of thinking for awhile before 
deciding, no, it really needs to be the case that invoking an inherited method 
means executing the method body in its original context.)

Now, all that said, we could say by fiat that `getClass` is special and value 
types aren't allowed to invoke it. YAGNI. Except...

I might want to write code like:

<T extends Point.ref> void m(T arg) {
    if (arg.getClass() == Point.class) System.out.println("I'm a value!");
    else System.out.println("I'm a box!");
}

Someone might think this, but they can just ask themselves whether 
`int/Integer` work like that. They don't, so this doesn't either.

int/Integer are a starting point, but our goal is to offer something more.

In particular, we want universal generics: when I invoke m and pass it a Point, 
it must be the case that T=Point, not T=Point.ref. This is different than the 
status quo for int/Integer, where T=Integer.

The right way to interpret generic code is, roughly, to substitute [T:=Point] 
and figure out what the code would do. This is imprecise, because there are 
compile-time decisions that aren't allowed to change under different 
substitutions. (For example, we don't re-do overload resolution for different 
Ts, even if it would get different answers.) But, for our purposes, it should 
be the case that you can imagine 'arg' being a value, not a reference, and this 
code having intuitive behavior.

So the ref/val model says that 'arg' is an object (handled by value, not by 
reference) and its 'getClass' method returns the class of the object.

The value/object model says that 'arg' is a value and its 'getClass' method 
exists. And I guess it returns Point.class.

(If we really thought `getClass` was poison, I guess at this point we could say 
by fiat that type variable types aren't allowed to access `getClass`. But... 
`getClass` really is a useful thing to invoke in this context.)

An implication of universal generics is that there needs to be some common 
protocol that works on both vals and refs. In the val/ref model, that protocol 
is objects: both vals and refs are objects with members that can be accessed 
via '.'. In the value/object model, I'm not quite sure how you'd explain it. 
Maybe there's a third concept here, generalizing how values and objects behave.

Reply via email to