The interfaces IdentityObject and ValueObject must die !
John Rose
john.r.rose at oracle.com
Thu Jan 27 01:14:04 UTC 2022
On 26 Jan 2022, at 16:55, Dan Smith wrote:
>> On Jan 26, 2022, at 4:55 PM, John Rose <john.r.rose at 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`).
More information about the valhalla-spec-observers
mailing list