<!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>