Scope for JEP 468: Derived record creation
Remi Forax
forax at univ-mlv.fr
Fri Mar 1 15:39:55 UTC 2024
----- Original Message -----
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Chris Bouchard" <chris at upliftinglemma.net>, "amber-dev" <amber-dev at openjdk.org>
> Sent: Friday, March 1, 2024 3:24:02 PM
> Subject: Re: Scope for JEP 468: Derived record creation
> I do understand that there are some people who want by-keyword
> invocation *so badly* that they are willing to write bad code to gain
> the illusion of doing so. Your example is the canonical example:
> MyRecord has no good default, but some people will be tempted to expose
> (null, null) as the default anyway (instead of rejecting those in the
> constructor) just so they can "stick it to the compiler." (Note that
> this is fine if MyRecord actually has a reasonable default, which some
> records do, or if you create a constructor that accepts the required
> components and fills in sane defaults for the rest.)
>
> Put more bluntly, some programmers overvalue superficial syntactic
> preferences so badly that they are willing to compromise the safety and
> correctness of their code -- and will pat themselves on the back for
> their cleverness while they do it.
>
>> People have been asking for keyword parameters in Java for years. Whether or not
>> this block syntax is what the language designers would choose for keyword
>> parameters, I think that if we introduce this feature without some way to
>> construct new instances it will become the de facto syntax. It's just too
>> tempting.
>
> Oh, I get people want this. And that bad programmers will surely do
> this. (And then they'll complain that they can't do it with classes, or
> with factories, or that it interferes with compatible migration from
> records to classes.) But I don't think we should let their threatened
> bad behavior drive language design decisions.
>
> People think they want keyword parameters, but that's not really what
> they want -- they just don't realize it yet. What they really want is
> keyword parameters *plus being able to omit parameters that have a
> default*. These two may sound like almost the same thing, but the
> difference in reality is huge. And as someone who has spent more time
> thinking about this problem in Java than probably anyone else, I promise
> you this is not the triviality it may seem. So our antipathy to named
> invocation is not just that it is a "meh" feature; it is that its cost
> and benefit are way out of line with each other.
Both can be unified if we introduced a weird syntax to declare and use a value record as parameter or return type.
This is a little bit similar to an anonymous class because the value record is scoped to the method but it has a name so javac error messages and binary compatibility are not an issue
record Circle(int x, int y, int radius) {
Circle(Point(int x, int y)) {
this(x, y, 1);
}
}
which is translated to:
record Circle(int x, int y, int radius) {
Circle(Circle$$Point $p) {
int x = $p.x;
int y = $p.y; // or Circle$$Point(int x, int y) = $p;
this(x, y, 1);
}
}
// like an anonymous class, the EnclosingMethod is Circle(Circle$$Point)
value record Circle$$Point(int x, int y) {}
and at use site
new Circle({ x = 3; y = 4; })
we need inference so { x = 3; y = 4; } is equivalent to new Circle$$Point(3, 4).
The exact details are fuzy but as you know, this also solve
- how to write a de-constructor
Point(int x, int y) deconstructor() {
return { x = this.x; y = this.y; };
}
and it makes the syntax looks like the inverse of the constructor syntax
- how to returns multiple values
Div(int quotient, int remainder) div(int value, int divisor) {
return { quotient = value / divisor; remainder = value % divisor; };
}
- and even how to declare tuples, if we allow (3, 4) to be inferred as new Circle$$Point(3, 4) too.
The idea is that a value record is so lighweight at runtime that having a syntax that mix the declaration of the value record and its use make sense.
I also found this idea stupid at first but it keep popping in a lot of use cases.
Rémi
>
>
>
>
>
> On 2/29/2024 7:39 PM, Chris Bouchard wrote:
>> Hi there. I'm new to this mailing list, but I had a similar thought to Swaranga
>> recently when this JEP was posted on Reddit.
>>
>> Personally, my primary concern isn't having nice syntax for constructing
>> records. It's that if we *don't* provide nice syntax for constructing records,
>> people will be incentivized to hack it in by overloading the new "with" syntax.
>> For example, consider something like
>>
>> record MyRecord(String foo, String bar) {
>> MyRecord {
>> // We'd prefer to reject nulls entirely, but we'll allow all nulls
>> // to have a "blank" object.
>> if (allNull(foo, bar))
>> return;
>>
>> // With that out of the way, validate our *actual* constraints.
>> if (anyNull(foo, bar) || foo.length < 1 || bar.length < 1)
>> throw MyDomainException();
>> }
>>
>> public static MyRecord blank() {
>> return MyRecord(null, null);
>> }
>> }
>>
>> Now I can say
>>
>> var value = MyRecord.blank() with {
>> bar = "World";
>> foo = greeting(bar);
>> };
>>
>> Except that now, because my model didn't have a natural "blank" value, I've
>> added an *un*natural one in the interest of ergonomics. This "blank" object has
>> to be a valid record value. And since the with block's variables are all
>> initialized to null, users can accidentally run into problems with partial
>> initialization.
>>
>> var value = MyRecord.blank() with {
>> // Whoops, forgot to initialize bar so it's still null.
>> // bar = "World";
>> foo = greeting(bar); // NPE
>> }
>>
>> On the other hand, if we provided an actual way to initialize a fresh record
>> using this block syntax, we could say that all variables in the block are
>> unitialized to start and must be assigned before use. And this initial state
>> wouldn't have to be accepted by the constructor.
>>
>> People have been asking for keyword parameters in Java for years. Whether or not
>> this block syntax is what the language designers would choose for keyword
>> parameters, I think that if we introduce this feature without some way to
>> construct new instances it will become the de facto syntax. It's just too
>> tempting.
>>
>> Thanks for your time and attention,
>> Chris Bouchard
>>
>> On Thu Feb 29 23:11:32 UTC 2024, Brian Goetz wrote:
>>> While such a feature is possible, I am not particularly excited about it
>>> for several reasons. If the goal is "I want to construct using named
>>> instead of position arguments" (which I think is your goal), it's a
>>> pretty verbose syntax; a better one would be
>>>
>>> new R(a: 1, b: 2)
>>>
>>> But that's a much smaller concern. The bigger one is that I don't think
>>> the language is improved by having a named parameter mechanism for
>>> records, but for nothing else (not for instantiating class instances,
>>> not for invoking methods.) While it might seem "better than nothing",
>>> having two different ways to do something, but one of them only works in
>>> narrow situations, is as likely to be frustrating than beneficial. If
>>> the payoff is big enough, then it might be a possibility, but
>>> "invocation by parameter name" is nowhere near that bar.
>>>
>>> Finally, if it works for records but not for classes, it makes it harder
>>> to refactor from records to classes, since there will be use sites that
>>> have to be adjusted.
>>>
>>> So I think for the time being, the answer is "no", though if we run out
>>> of things to work on, we might reconsider.
>>>
>>> On 2/29/2024 4:34 PM, Swaranga Sarma wrote:
>>>> The JEP looks really promising. However I am wondering if there will
>>>> be a separate JEP for creating new records from scratch with similar
>>>> syntax.
>>>>
>>>> The current JEP states that its goals are to enable creating records
>>>> from an existing record and for that it seems sufficient. But I would
>>>> also love to be able to create new records from scratch using the same
>>>> syntax. Something like:
>>>>
>>>> var circle = new Circle with {
>>>> radius = 0.5f;
>>>> center = new Center with {
>>>> x = 0;
>>>> y = -1;
>>>> z = 8;
>>>> };
>>>> };
>>>>
>>>> Originally I had asked Brian Goetz about record literals and they
>>>> seemed like a possibility but withers seem like a more general feature
>>>> so I am hoping something like this would be supported in the future.
>>>>
>>>> Regards
> >>> Swaranga Sarma
More information about the amber-dev
mailing list