<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <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 class="moz-cite-prefix">On 11/26/2024 5:13 AM, Øyvind Kvien
      wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:CAKipqtLvnHZa5bQhHWYXso-w9XCDQxYzMttXFGnHzwik5vC_SQ@mail.gmail.com">
      
      <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://myservice/test" moz-do-not-send="true" class="moz-txt-link-freetext">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://myservice/test" moz-do-not-send="true" class="moz-txt-link-freetext">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>
  </body>
</html>