On 26 Jan 2022, at 16:55, Dan Smith wrote:

On Jan 26, 2022, at 4:55 PM, John Rose <john.r.r...@oracle.com> wrote:

Independently of that, for the specific case of Object

, having a query function Class.instanceKind

, which returns “NONE” for abstracts else “VALUE” or “IDENTITY”, would encode the same information we are looking at with those marker interfaces.

Right, so you're envisioning a move in which, rather than 'obj instanceof ValueObject', the dynamic test is 'obj.getClass().instanceKind() == VALUE'.

 For dynamic testing (my #3), sure, these are equivalent.

But the contract for a method is more flexible than the contract of a marker interface.

In particular, instanceKind

is not required to report the same thing for T and U when T<:U but marker interfaces are forced to be consistent across T<:U. I think this is an advantage, precisely because it has more flexible structure, for the method rather than the marker interface.

I would expect that 'cls.instanceKind() == IDENTITY' has the exact same semantics as 'IdentityObject.class.isAssignableFrom(cls)': if a class claims to be an identity class, then all its instances (direct and via subclassing) are identity objects. I'm not seeing a sensible alternative.

How does this behave in the odd world in which direct instances of Object are identity objects?

'Object.class.instanceKind()' must return NONE, just as Object.class must not implement either IdentityObject or ValueObject.

That last “must” is necessarily true, but the second-to-last “must” is not necessarily true. That’s my point here.

A. I am aiming for `new Object().getClass() == Object.class`.
B. But it is also true that `new Object()` is an identity object (no caps yet). C. *If* the way to ask whether an object `x` is an identity object is a type test (`x instanceof IdentityObject`), *then* given step A. and B. `Object` must implement the marker `IdentityObject`. (Trouble is brewing now.) D. Meanwhile, some random non-identity value class `Point` ultimately subtypes `Object`. E. But (and here’s *exactly* where the exquisite regularity of inheritance patterns fails to meet our requirements) this means that `Point` must inherit the super type `IdentityObject` because of step C. F. Thus, inheritance of a reflective marker would force the query `new Point() instanceof IdentityObject` to be true. It’s an impasse!

We can break the impasse at step C by *not* using marker interfaces *because they inherit*. The reflective query I’m proposing, to a class C, applies *only to concrete instances of that precise class C* and not to instances of C’s subtypes.

A different condition is the limitation that *all subtypes* of C must be values, or must be identities. Asking about all subtypes is a slightly different question. Using a marker interface conflates those two questions.

Given one of these oddball objects, 'obj.getClass().instanceKind()' will, naturally, return NONE. Which is surprising, breaking the expectation that there are only two possible results of this expression. Just as these objects would break the expectation that every object is either 'instanceof IdentityObject' or 'instanceof ValueObject'.

I keep saying this: how we handle the 'new Object()' problem doesn't seem to me to have any impact on how we encode "I'm an identity class". It's not a discussion that belongs in this email thread.


If the marker interfaces also have little use as textual types (e.g., for bounds and method parameters) then I agree with Remi. Ditch ‘em.

I outlined many ways in which we're making use of these interfaces. Static types is just one. Getting rid of them isn't as easy as "ditch 'em", it would involve redesigning for all of those use cases (plus any I forgot), and coming up with something that is compellingly better. (This is an invitation, for anyone interested in proposing something specific...)

That’s fair; you are right that the whole list needs to be covered. My point about “ditch 'em” is simply that if all of the use cases of marker interfaces involve reflective queries (rather than formal arguments that textually name the interfaces) then we can use the above query methods instead. And I don’t think I’m too far off, given that `Serializable` and `Cloneable` are *never* (or 0.001%) used textually to declare formal variables; they always show up as `Serializable.class` or `x instanceof Serializable` or `extends Cloneable`.

I think one of your use cases that isn’t covered well by the reflective query I suggested is declaring a type that extends `IO` or `VO`, thus constraining all concrete types that extend that type. We could keep this as a reduced portfolio for *explicit* `IO` and `VO` but still divert the *query* to a method. So the marker interfaces are *one way* to force the query to return a specific kind other than `NONE` (or `BOTH`).

Reply via email to