Alternative to IdentityObject & ValueObject interfaces
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Mar 23 10:23:26 UTC 2022
On 22/03/2022 23:56, Dan Smith wrote:
> Other abstract classes and interfaces are fine being neither (thus supporting both kinds of subclasses).
I feel that for such a proposal to be really useful (but that's true for
the interface-based approach as well IMHO), you need a way for the _use
site_ to attach an identity vs. value annotation to types that can
feature both polarities (Object, interfaces, value-compatible abstract
classes).
It's perfectly fine to have identity vs. non-identity as a declaration
property, for the cases whether that works. E.g. an ArrayList instance
will always have identity. An instance of a `value class Point` will
always be identity-less. Using modifiers vs. marker interfaces here is
mostly an isomorphic move (and I agree that adding modifiers has less
impact w.r.t. compatibility).
But it feels like both interfaces and decl-site modifiers fall short of
having a consistent story for the "neither" case. I feel we'd like
programmers to be able to say things like:
```
class Foo {
identity Object lock;
void runAction(identity Runnable action) { ... }
}
```
So, I believe the modifier idea has better potential than marker
interfaces, because it scales at the use site in ways that marker
interfaces can't (unless we allow intersection types in declarations).
But of course I get that adding a new use-site modifier (like `final`)
is also not to be taken lightly; aside from grammar conundrums, as you
say it will have to be tracked by the type system.
Stepping back, you list 4 use cases:
> - Dynamic detection
>
> - Subclass restriction
>
> - Variable types
>
> - Type variable bounds
IMHO, they are not equally important. And once you give up on "variable
types" (as explained above, I believe this use case is not adequately
covered by any proposal I've seen), then there's a question of how much
incremental value the other use cases add. Dynamic detection can be
added cheaply, fine. I also think that, especially in the context of
universal generics, we do need a way to say: "this type variable is
legacy/identity only" - but that can also be done quite cheaply. IMHO,
restricting subclasses doesn't buy much, if you then don't have an
adequate way to restrict type declarations at the use sites (for those
things that cannot be restricted at the declaration), so I'd be also
tempted to leave that use case alone as YAGNI (by teaching developers
that synchronizing on Object and interface types is wrong, as we've been
already trying to do).
P.S.
While writing this, a question came up: let's say I have a generic class
like this:
```
class IdentityBox<identity T> { ... }
```
Is IdentityBox<Runnable> a well-formed parameterized type? Based on your
description I'm not sure: Runnable has the "neither" polarity, but T
expects "identity". With marker interfaces this will just not work. With
modifiers we could perhaps allow with unchecked warning?
I think it's important that the above type remains legal: I'd expect
users to mark their type-variables as "identity" in cases where they
just can't migrate the class implementation to support universal type
variables. But if that decision results in a source incompatible change
(w.r.t. existing parameterizations such as IdentityBox<Runnable>), then
it doesn't look like a great story migration-wise.
Maurizio
More information about the valhalla-spec-observers
mailing list