<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4" face="monospace">No, in part because we don't yet
have a good enough solution, and in part because there are other
higher priorities at the moment. <br>
</font><br>
<br>
<div class="moz-cite-prefix">On 11/26/2024 1:09 PM, Øyvind Kvien
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAKipqt+Xyy0QebxtbOagSR9EX9cwjj+V-_Zc1Ngz9jMZ_aBw4g@mail.gmail.com">
<div dir="ltr">Thank you for the answer. Yes nominal invocation
and default parameter values would be a better approach. Is
there a JEP drafted for this feature (I can't find one)?</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Tue, Nov 26, 2024 at
4:00 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" moz-do-not-send="true" class="moz-txt-link-freetext">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div> <font size="4" face="monospace">This has been well
covered in several previous mails. <br>
<br>
The reason this request has not seen favorable reception
is because it is actually a request for a _different_
feature -- just with a worse syntax. What you really want
is nominal (rather than positional) invocation of
constructors (like, for example, `new Foo(a: 1, b: 2)`).
And we totally get that people really, really want this.
We're just not going to cram a bad version of it into the
language because people want it so badly. <br>
<br>
Why this would be a bad version of nominal invocation is
evident in your mail: you talk about using withers with
"default" constructors, but records don't have default
constructors. And you point out why: that often there is
no good default value for record components. So in the
solution you are suggestion, there is no "withing" at all;
it is not deriving one record from another. It is just
trying to attach an unrelated feature to the wither
proposal, because it kinda sorta looks like it (and it
feels like the train is leaving the station and if we
don't throw this extra feature on it, we might never get
it!) This is not the way to evolve the language.<br>
<br>
Compared to nominal invocation, the `with` version is:<br>
<br>
- More confusing, because there is no withing going on <br>
- More verbose<br>
- Offers no path to nominal invocation for instance
members (e.g., `x.foo(a: 1)`) <br>
<br>
So it is a much worse version of "nominal invocation."
Its the sort of thing people would be happy about for
about five minutes, but would likely be unhappy about
thereafter.<br>
<br>
And, when we say what people really want is nominal
invocation, what we mean is that they really want nominal
invocation _with defaultable parameters_. Because this is
what gets rid of the builders.<br>
<br>
So yes, we deeply get that there is a pain point here.
But this is not the solution.<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</font><br>
<br>
<div>On 11/26/2024 5:13 AM, Øyvind Kvien wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">I see there're no plans of including the
default constructor with withers for record creation,
and a manually created static builder has previously
been suggested instead with default values in the
constructor. In my opinion ommiting the default
constructor to be used with withers would be a big
mistake.
<div><br>
<div>A very common scenario in smaller services is to
use simple data structures to pass data to the api
of other services and message brokers. In these
cases the data contains mostly primitive types and
(these are not domain models). Setting default
values manually in the default constructor, as
suggested would be very tedious and lots of typing.
Also many times a default value is not even possible
since it could mean setting the object in a wrong
initial state. Forcing the use of default values I
think is a bad approach.</div>
<div><br>
It would therefore be immensely useful if the
default record constructor could be used with
withers. The compiler needs of course to enforce
that all parameters in the default constructor are
set.<br>
<br>
Below is a real world example of how building a
request body to an internal service in my
organization, used for getting access tokens for
test purposes, would look like using withers and the
default constructor. The following records describes
the simple data structure for the request body to
the api of the service (which gets serialized to
json).
<div><br>
</div>
<div> record RequestBody(<br>
String audience,<br>
boolean withoutDefaultClientClaims,<br>
boolean withoutDefaultUserClaims,<br>
boolean createDPoPTokenWithDPoPProof,<br>
ExpirationParameters
expirationParameters,<br>
ClientClaimsParameters
clientClaimsParameters,<br>
@Nullable UserClaimsParameters
userClaimsParameters,<br>
@Nullable DPoPProofParameters
dPoPProofParameters) {<br>
}<br>
<br>
record ExpirationParameters(<br>
int expirationTimeInSeconds) {<br>
}<br>
<br>
record ClientClaimsParameters(<br>
List<String> scope,<br>
String orgnrParent,<br>
String clientId,<br>
String clientName,<br>
String jti) {<br>
}<br>
<br>
record UserClaimsParameters(<br>
String pid,<br>
String hprNumber,<br>
String name,<br>
String givenName,<br>
String middleName,<br>
String familyName,<br>
String securityLevel,<br>
String assuranceLevel,<br>
String amr) {<br>
}<br>
<br>
record DPoPProofParameters(<br>
String htuClaimValue,<br>
String htmClaimValue) {<br>
}</div>
</div>
</div>
<div><br>
</div>
<div>Using withers with the default constructor would
look as suggested in the method below. It's very easy
to visualise the json structure that the RequestBody
record get serialized into when the parameter names
are present.</div>
<div><br>
</div>
<div>Also there's no need to use default values which is
a big win, and the compiler enforces that all
parameters are set. This way the record is never
instantiated in a wrong state.</div>
<div><br>
</div>
<div>Note that no parenthesis are used after 'new
RequestBody' for the default constructor as a syntax
suggestion.<br>
<br>
public static RequestBody
createRequestBodyUserToken(<br>
@Nullable String clientId,<br>
@Nullable String userNin<br>
) {<br>
return new RequestBody with { // No
parenthesis after 'new RequestBody' for the default
constructor.<br>
audience = "audience";<br>
withoutDefaultClientClaims = true;<br>
withoutDefaultUserClaims = true;<br>
createDPoPTokenWithDPoPProof = true;<br>
expirationParameters = new
ExpirationParameters with {<br>
expirationTimeInSeconds = 300;<br>
};<br>
clientClaimsParameters = new
ClientClaimsParameters with {<br>
scope = List.of("openid", "scope");<br>
orgnrParent = "12345";<br>
clientId = clientId != null ? clientId
: "13edc8d1-3fa2-425a-9b53-c346df79e589";<br>
clientName = "SmokeTest";<br>
jti = UUID.randomUUID().toString();<br>
};<br>
userClaimsParameters = new
UserClaimsParameters with {</div>
<div> pid = userNin != null ? userNin :
"12345678912";<br>
hprNumber = "987654";<br>
name = "Half Badger";<br>
givenName = "Half";<br>
middleName = "";<br>
familyName = "Badger";<br>
securityLevel = "4";<br>
assuranceLevel = "high";<br>
amr = "pwd";<br>
};<br>
dPoPProofParameters = new
DPoPProofParameters with {<br>
htuClaimValue = "GET";<br>
htmClaimValue = "<a href="https://urldefense.com/v3/__https://myservice/test__;!!ACWV5N9M2RV99hQ!ImZVGoRgncmogAFG0n7i4KRdOhw1kyEzqsIO-IXBkta5m2B1ycwHwb1WWaPmHcZtDQIdeFH1JKGiMmeEwA$" target="_blank" moz-do-not-send="true">https://myservice/test</a>";<br>
}<br>
};<br>
}</div>
<div><br>
</div>
<div>The alternative, as of today, is to first
instantiate each record separately and then lastly
instantiate the RequestBody and return it. It's harder
to read as the parameter names get lost.<br>
<br>
private static RequestBody
createRequestBodyUserToken(<br>
@Nullable String clientId,<br>
@Nullable String userNin<br>
) {<br>
var expirationParameters = new
ExpirationParameters(<br>
300<br>
);<br>
<br>
var clientClaimsParameters = new
ClientClaimsParameters(<br>
List.of("openid","scope"),<br>
"12345",<br>
clientId != null ? clientId :
"13edc8d1-3fa2-425a-9b53-c346df79e589",<br>
"SmokeTest",<br>
UUID.randomUUID().toString()<br>
);<br>
<br>
var userClaimsParameters = new
UserClaimsParameters(<br>
userNin != null ? userNin :
"12345678912",<br>
"987654",<br>
"Half Badger",<br>
"Half",<br>
"",<br>
"Badger",<br>
"4",<br>
"high",<br>
"pwd"<br>
);<br>
<br>
var dPoPProofParameters = new
DPoPProofParameters(<br>
"GET",<br>
"<a href="https://urldefense.com/v3/__https://myservice/test__;!!ACWV5N9M2RV99hQ!ImZVGoRgncmogAFG0n7i4KRdOhw1kyEzqsIO-IXBkta5m2B1ycwHwb1WWaPmHcZtDQIdeFH1JKGiMmeEwA$" target="_blank" moz-do-not-send="true">https://myservice/test</a>"<br>
);<br>
<br>
return new RequestBody(<br>
"audience",<br>
true,<br>
true,<br>
true,<br>
expirationParameters,<br>
clientClaimsParameters,<br>
userClaimsParameters,<br>
dPoPProofParameters<br>
);<br>
}</div>
<div><br>
</div>
<div>I therefore hope that the default record
constructor can be included with withers! It would be
very useful when working with any type of api.</div>
<div><br>
</div>
<div>Regards</div>
<div>Øyvind Kvien</div>
<div><br>
</div>
</div>
</blockquote>
<br>
</div>
</blockquote>
</div>
</blockquote>
<br>
</body>
</html>