[records] translation strategy (factory methods)
Tagir Valeev
amaembo at gmail.com
Fri Jan 10 16:33:13 UTC 2020
Agreed with Brian.
With best regards,
Tagir Valeev.
On Fri, Jan 10, 2020 at 11:22 PM Brian Goetz <brian.goetz at oracle.com> wrote:
>
> There is a whole category if "ooh, shiny new language construct, this is an opportunity to slide in some new features." We saw this with lambdas -- "can you please make the parameters of lambdas implicitly final." The motivation for such requests is obvious; we wish all parameters could be implicitly final (perhaps with an opt-out), but we can't turn back the clock, so we console ourselves with "well, at least I can have _some_ finality.) But this consolation is often disappointing, because it creates (a) challenges for migration between existing constructs (anon classes) and the new construct (lambdas), often in both directions, and (b) requires users to keep track of complex, history-driven rules for when they can use a feature ("oops, can't have static fields in inner classes.") These are often a siren song, and are best avoided.
>
> Turning to records, an example of a feature we've rejected on the basis of this siren song: Named parameter invocation (`new Point(x: 1, y: 2)`.) Yes, parameter names are part of the API, so this becomes easier for records than constructors/statics (which is easier, in turn, than for instance methods.) Instead, we specified that record component names are always reified through reflection, so that if/when we ever get to a more general story for named invocation, records will be ready.
>
> While the user burden is less in this case, compiling `new R(...)` to a factory invocation feels like pushing on the wrong end of the "constructor rehabilitation" lever, because doing so will create binary compatibility trouble for records that want to migrate back to classes. (Like with enums, this is not 100% compatible, because of the supertype (Enum/Record), but like with enums, it is not uncommon to hit the wall and want to refactor back to classes. Let's not make this harder.)
>
> So my vote is #0. But, if we have a story for moving _all_ classes towards factory construction, then we should make sure records play well. I don't think we have to do anything now for which we'd regret inaction, because whatever problem we have with legacy records, we'll already have with every class ever written.
>
>
>
>
> On 1/10/2020 1:36 AM, John Rose wrote:
>
> Another bit of translation strategy: What does “new R(a,b,c)” compile
> to, at the bytecode level? At some point we will cut over to factory methods,
> for at least some types (inlines). Should be pre-empt this trend for records,
> and mandate that records are *always* created by factory methods?
>
> The benefit is that records can be evolved to inline types without
> recompiling their *clients*. The risk is that (if we don’t take this
> option) that we will have records which are constrained to be identity]
> types *until all clients are recompiled*. (No I don’t believe that you
> can translate “new;dup;invokespecial<init>” into “invokestatic”.
> Sorry, that's a pipe dream.) This seems sad, given that records are
> partially inspired by value types.
>
> (From my 2012 blog on value types: “A value type is immutable… The
> equals and hashCode operations are strictly component-wise.” Kind
> of like a record. Meanwhile, inlines in Valhalla give component-wise
> meaning to acmp not equals and identityHashCode not hashCode.)
>
> I see three options regarding factories (apart from the above pipe dream):
>
> 0. Do nothing. Records are just abbreviated classes. Whatever works
> or doesn’t for evolving general identity classes to inline classes works
> or doesn’t for records. No special benefit, in this vein, to declaring
> something a record.
>
> 1. Define a factory, on top of today’s JVM. Records use a hidden API,
> desugaring “new R(a,b)” to a static “R.$make$(a,b)”.
>
> 2. Define a factory, anticipating Valhalla, using a JVM-defined entry point.
> The expression “new R(a,b)” compiles to “invokestatic R.<init>(int,int)R”,
> preceded by a push of a and b. This requires a (relatively shallow) change
> to the JVM to double down on the name “<init>” as the factory behind a
> constructor.
>
> The advantage of 2 is that, if we correctly predict that “<init>” is the claimed
> factory method for value types, then records can be evolved to inlines out
> of the box. The advantage of 1 is that we don’t need to make that prediction.
>
> There’s also this:
>
> 3. Do 1. but when 2. becomes an option create an auto-bridge from “$make$”
> to “<init>”. New code doesn’t need the bridge because it compiles to call
> “<init>” out of the box.
>
> Here’s a gut check for this group: Are we confident enough with Valhalla
> that we can settled on “invokestatic <init>” as the new dance for creating
> an instance that may or may not be an inline?
>
> — John
>
>
More information about the amber-spec-experts
mailing list