On 19 Jul 2022, at 10:54, Kevin Bourrillion wrote:

…My own answer to the question has been
Understanding
classes and types in Java
<https://docs.google.com/document/d/1G5dNQ0kQwA5zefGdP_nvFJByb63QNlz0XiSjltiJM84/edit>
(comments
welcome).
I see a class as a way to "configure" and feed behavior into a type (apart from classes *also* serving as bundles of static members). It's a fairly
subservient relationship, which feels right to me.

Yes, I like your document (at about a 90% level). It’s helpful to mention it here.

I think it’s a good observation that static members are properties only of classes while non-static members are primarily properties of types.

(All members are in classes to start with but in your document non-statics quickly lift to types in practical usage. There are surely times when members stay in their class. Reflection comes to mind.)

I also like your observation that the principal type of a class can be obtained by asking for the type of `this`. It’s clean.

It might be too clean for us, in fact, because the observation is likely to clash with some very different but also reasonable expectations, which would ask that the principal type of a class be the meaning of `C` (unadorned by `.ref` or `.val`), or that the principal type of a class be represented by the mirror which you get when you call `Object::getClass` on an instance.

I think I would like all of the above questions to be answered by `C.ref`, even though there are plenty of times when someone will propose or expect that `C.val`, or something else, is the answer.

(Oh, and please please do not have `Object::getClass` return different values for variables of type `C.ref` and `C.val`; I think I see that suggested from time to time!)

Your appeal to `this` has a special benefit when `C` is generic: It captures the most general type possible, of the form `C<T>`.

(Except for conditional methods if we ever do those; then `this` might have a conditional bound on a parameter. The root problem is that `T` will probably mean something subtly different in a conditional method, so `C<T>` doesn’t mean just one type everywhere in `C`.)

(Sorry for digression: you could also say one class engenders many array
types, though. I think it helps to fully distinguish predefined,
user-defined, and composed types. Setting aside value classes temporarily:
each class directly defines just one type, which is the type of `this`
inside the class itself (the "implicit type", or the "this-type"). That's the all-important type whose member signatures are seen in the class and
whose supertypes are seen in the class signature. Other types can be
composed out of the defined types: array types, type variables,
intersection types I guess, and relevant to us here, all *other*
parameterized types beyond the implicit type. That is, imho it's most
fruitful to understand those parameterized types as deriving from the
implicit type/"this-type", with member signatures and supertypes being
calculated from that implicit type via substitution, rather than to see
them all as popping directly off of the generic class.)

Yup, when is a related type a true companion, and when is it just a projection? We get to define this, and then we have to live with it.

It’s an interesting outcome (of your `this` position) that `C<T>`, out of all the generic instances of `C`, is elevated to principal position, and all other `C<U>` are mere projections of `C`.

(Surely you already considered and rejected the following alternative choice of narrative in your document, which I will state here FTR: The principal type of `C`, when `C` is generic, is its *raw type*. That is much less useful for speaking about the type of expressions derived from `this`, but it aligns much more closely with the other “questions” I alluded to above: “What is the type denoted by merely the class name `C`?” And “What is the mirror returned from `Object::getClass` when invoked on an instance of class `C`?”)

I see some sense in your argument, but I still can't think of a reason I'd want to see `ClassName.ref` in source code. It seems like that can't add
any information.

I mean it can adds a certain connotation (“stylized clarity” as I said) to the code. Have you ever written a fully-qualified name where it wasn’t necessary? (I have, when I wanted to emphasize where the symbol came from: Such emphasis is connotation not denotation.) Have you ever written `public` on an interface member where you didn’t need to? Again, I’d call that choice a matter of stylized clarity.

Depending on how type inference works, `ClassName.ref` vs `ClassName` might affect TI, as `List<ClassName.ref>` vs. `List<ClassName>`. This is, I think, the case with certain drafts of Valhalla-related generics.

I made a sly reference to null-inference. As with type inference, I could imagine designs of NI where `ClassName.ref` vs `ClassName` produces a different inference about null. Suppose there’s some way of saying, for `ju.Optional`, that only a dope would make null values of that reference type. Then `Optional.ref` could possibly be a way of saying, “I’m that dope, bear with me.”

…
 - Maybe: For any type variable T (in specialized generics?), T.val
   also names a type.


If I ever see `T.val` (except maybe the case of `T.val[]`??) I will assume
some kind of templating must be going on, since we'll all have learned
early on that there is no polymorphic interaction with values. Is that your
expectation too?

I’m imagining, at least, some sort of additional “leakage” of ref/val distinctions into the scope of `T`. We have such leakage already otherwise `T.ref` wouldn’t be useful; it happens when a generic API is bound to type arguments and `T` looks like `C.val`. I think the consensus is that the use cases don’t support doing the reverse, of allowing `T.val` to mean `C.val` when `T` is `C.ref`, but it’s logically possible isn’t it? And if so a use case may show up.

Independently, something like what Remi discusses, of flattening to val-type inside a generic bound to a ref-type, could be a use of `T.val`. I think you surmised that: `new T.val[n]` could be an optimistic dynamic buffer, if the actual type `C.val[]` were somehow available at that point. That would require even more “leakage” of information about type arguments beyond the API of a generic and into its method bodies and maybe even field types. Eventually you would use such information to “fill in templates”, including flattening fields to `C.val`.

Reply via email to