<div dir="ltr"><div class="gmail_default" style="font-family:monospace">Hello Swaranga,</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">I think you are only looking at the general, most simplistic case, then pointing to that as potential evidence of why withers are not as good of a fit as nominal params.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">The difference that withers bring to the table is that you can run loops, statements, etc., in a wither. So, that gives you far more flexibility than nominal parameters to create the values you need, and better yet, in a new scope that keeps helper variables for only as long as needed.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">With that in mind, Nominal params are solving a similar, but different problem. They are simply allowing you to specify the exact variable name that you are giving this value to, as opposed to using position.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">It's sort of like the difference between an if statement and a ternary operator. They serve similar goals, but their Venn Diagram of intents is different. Or an if statement vs a switch statement. Again, overlap, but not the same.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">For more complex destructuring and restructuring, you would use a wither.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">For far more simplistic variable assignment (especially in cases where there isn't an old instance of the same type to extract values from), you use named parameters.</div><div class="gmail_default" style="font-family:monospace"><br></div><div class="gmail_default" style="font-family:monospace">Let me know if that is not clear.<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Nov 26, 2024 at 6:57 PM Swaranga Sarma <<a href="mailto:sarma.swaranga@gmail.com">sarma.swaranga@gmail.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 dir="ltr"><div>Brian, I totally get why withers are a bad way to introduce nominal parameters for the general case. But say in future, you do come up with a solution for nominal parameters, would then withers, instead, stick out like an unnatural/redundant feature in the language that exists only to clone objects? Say we have</div><div><br></div><div>record Rec(int a, int b) {}</div><div><br></div><div>var one = new Record(a: 10, b:42);</div><div>var two = new Record(a: one.a(), b: 24)</div><div><br></div><div>Nominal parameters, by design, seem to also address the problems solved by withers to a degree. Sure, I still have to specify every parameter in the nominal world and it is a little more verbose so they are not directly comparable, but it still seems very close. Would like to know how you are looking at it.</div><div><br clear="all"></div><div><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>Regards<br></div><div>Swaranga</div></div></div></div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Nov 26, 2024 at 10:24 AM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" target="_blank">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"><u></u>

  
  <div>
    <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>On 11/26/2024 1:09 PM, Øyvind Kvien
      wrote:<br>
    </div>
    <blockquote type="cite">
      
      <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" target="_blank">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">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">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>
  </div>

</blockquote></div>
</blockquote></div>