Evolving instance creation
Kevin Bourrillion
kevinb at google.com
Tue Mar 1 13:56:31 UTC 2022
Seems like this decision is trending in the direction I'd prefer already,
but here's some argumentation that *might* be helpful from the
programming-model perspective.
On Tue, Feb 22, 2022 at 1:17 PM Dan Smith <daniel.smith at oracle.com> wrote:
One of the longstanding properties of class instance creation expressions
> ('new Foo()') is that the instance being produced is unique—that is, not
> '==' to any previously-created instance.
>
I'll argue that this is an incidental association only.
Note that `new` has simply never been *needed* for identityless types
before; for those (all 8 of them), literals and binary expressions and
things had us covered. So `new` has so far seemed associated with
identityful types. But I think the expectation quoted here clearly comes
from the identity-type-ness, not from the `new` keyword. If we use `new`
with identityless objects or values, a distinct-identity expectation simply
doesn't apply.
Plus as Remi says, changing to that type changes `==` into a different
operator with the same name. So I think that this:
new Point(1, 2) == new Point(1, 2) // always true
>
.... is entirely *un*problematic!
Dan H. says, "`new` carries the mental model of allocating space" -- again,
I think it's incidental. Because the *point of introducing *identityless
types is that the distinction between creating and reusing (summoning from
the either somehow) vanishes. We shouldn't be able to distinguish those
cases.
As Dan S. says later,
I'd rather have programmers think in these terms: when you instantiate a
> value class, you might get an object that already exists. Whether there are
> copies of that object at different memory locations or not is
> irrelevant—it's still *the same object*.
But my reaction is: then to the programming model it *might as well just
look like creation*. It can't really "look like reusing" without forcing
the question of "reusing what from where?". It can only just look
*different*. But I don't think it needs to. (I think this is what Dan H
ends up supporting too.)
The main thing I think CICEs/`new` accomplish is simply to "cross the
bridge". Constructors are void and non-static; yet somehow we need to
*call* them
as if they're static and non-void! `new` gets us across that gap. This
seems to me like a special-snowflake problem that `new` is custom built to
address, and I would hope we keep it.
Seen this way, it's essential that this bridge-crossing happens *somewhere*,
but it doesn't necessarily mean constructors need to be spiffy public API.
It could be a dirty secret we hide within our static factory methods. And
we often do this, because public constructors *aren't* spiffy; they can't
have names, relying purely on argument types and order to disambiguate, and
they weirdly promise the caller never to return a subtype even though a
caller should never even care about that (because substitutability
principle).
Here are three approaches that I could imagine pursuing:
>
> (1) Value classes are a special case for 'new Foo()'
>
> This is the plan of record: the unique instance invariant continues to
> hold for 'new Foo()' where Foo is an identity class, but if Foo is a value
> class, you might get an existing instance.
>
(I've argued above it's not even a special case.)
Biggest concerns: for now, it can be surprising that 'new' doesn't always
> give you a unique instance.
This is the best kind of surprise! Because grappling with it points you
directly toward understanding what identityless classes are all about.
A couple more minor points about the factories idea:
> A related, possibly-overlapping new Java feature idea (not concretely
> proposed, but something the language might want in the future) is the
> declaration of canonical factory methods in a class, which intentionally
> *don't* promise unique instances (for example, they might implement
> interning). These factories would be like constructors in that they
> wouldn't have a unique method name, but otherwise would behave like ad hoc
> static factory methods—take some arguments, use them to create/locate an
> appropriate instance, return it.
>
Can you clarify what these offer that static methods don't already provide?
The two weaknesses I'm aware of with static factory methods are (1)
subclasses still need a constructor to call and (2) often you don't really
want the burden of naming them, you just want them to look like the obvious
standard creation path. It sounds like this addresses (2) but not (1), and
I assume also addresses some (3).
> (2) 'new Foo()' as a general-purpose creation tool
>
> In this approach, 'new Foo()' is the use-site syntax for *both* factory
> and constructor invocation. Factories and constructors live in the same
> overload resolution "namespace", and all will be considered by the use site.
>
It sounds to me like these factories would be static, so `new` would not be
required by the "cross the bridge" interpretation given above.
On Thu, Feb 24, 2022 at 7:40 AM Dan Heidinga <heidinga at redhat.com> wrote:
The rest of this is more of a language design question than a VM one.
> The `Foo()` (without a new) is a good starting point for a canonical
> factory model.
It's been mentioned somewhere in all this that there *can* be a static
method with that signature in the class. Evil to actually do that, yes. But
the mere fact it's possible makes this syntax a bit confusing imho.
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
More information about the valhalla-spec-observers
mailing list