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

Reply via email to