<div dir="ltr">Hello!<div><br></div><div>I can imagine that we have 

somewhere in the library a FunctionHelper interface like this:</div><div><br></div><div><pre style="color:rgb(0,0,0);font-family:"JetBrains Mono""><span style="color:rgb(0,0,128);font-weight:bold">interface </span>FunctionHelper<<span style="color:rgb(25,72,166)">R</span>, <span style="color:rgb(25,72,166)">T</span>> {<br>    <span style="color:rgb(0,0,128);font-weight:bold">default </span>Predicate<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">R</span>> is(<span style="color:rgb(25,72,166)">T </span>value) {<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span>r -> Objects.<span style="font-style:italic">equals</span>(map().apply(r), <span style="color:rgb(25,72,166)">value</span>);<br>    }<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">default </span>Predicate<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">R</span>> not(<span style="color:rgb(25,72,166)">T </span>value) {<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span>r -> !Objects.<span style="font-style:italic">equals</span>(map().apply(r), <span style="color:rgb(25,72,166)">value</span>);<br>    }<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">default </span>Consumer<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">R</span>> consume(Consumer<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">T</span>> consumer) {<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span>r -> <span style="color:rgb(25,72,166)">consumer</span>.accept(map().apply(r));<br>    }<br>    <br>    <span style="color:rgb(128,128,128);font-style:italic">// maybe more function adapters<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(128,128,128);font-style:italic">    </span>Function<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">R</span>, ? <span style="color:rgb(0,0,128);font-weight:bold">extends </span><span style="color:rgb(25,72,166)">T</span>> map();<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">static </span><<span style="color:rgb(25,72,166)">R</span>, <span style="color:rgb(25,72,166)">T</span>> FunctionHelper<<span style="color:rgb(25,72,166)">R</span>, <span style="color:rgb(25,72,166)">T</span>> forAccessor(Function<? <span style="color:rgb(0,0,128);font-weight:bold">super </span><span style="color:rgb(25,72,166)">R</span>, ? <span style="color:rgb(0,0,128);font-weight:bold">extends </span><span style="color:rgb(25,72,166)">T</span>> accessor) {<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span>() -> <span style="color:rgb(25,72,166)">accessor</span>;<br>    } <br>}</pre></div><div><br></div><div>Then we generate static fields that correspond to every record component:</div><div><br></div><div><pre style="color:rgb(0,0,0);font-family:"JetBrains Mono""><span style="color:rgb(0,0,128);font-weight:bold">record </span>Point(<span style="color:rgb(0,0,128);font-weight:bold">int </span>x, <span style="color:rgb(0,0,128);font-weight:bold">int </span>y) {<br>    <span style="color:rgb(0,0,128);font-weight:bold">public static final </span>FunctionHelper<Point, Integer> <span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">X </span>= FunctionHelper.<span style="font-style:italic">forAccessor</span>(Point::x);<br>    <span style="color:rgb(0,0,128);font-weight:bold">public static final </span>FunctionHelper<Point, Integer> <span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">Y </span>= FunctionHelper.<span style="font-style:italic">forAccessor</span>(Point::y);<br>}<br></pre></div><div>Then we can use them:<br></div><div><br></div><div><pre style="color:rgb(0,0,0);font-family:"JetBrains Mono""><span style="color:rgb(0,0,128);font-weight:bold">var </span>points = List.<span style="font-style:italic">of</span>(<span style="color:rgb(0,0,128);font-weight:bold">new </span>Point(<span style="color:rgb(0,0,255)">1</span>, <span style="color:rgb(0,0,255)">2</span>), <span style="color:rgb(0,0,128);font-weight:bold">new </span>Point(<span style="color:rgb(0,0,255)">3</span>, <span style="color:rgb(0,0,255)">4</span>), <span style="color:rgb(0,0,128);font-weight:bold">new </span>Point(<span style="color:rgb(0,0,255)">5</span>, <span style="color:rgb(0,0,255)">6</span>));<br>points.forEach(Point.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">X</span>.consume(System.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">out</span>::println));<br>points.stream().filter(Point.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">Y</span>.is(<span style="color:rgb(0,0,255)">4</span>)).forEach(System.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">out</span>::println);<br>points.stream().filter(Point.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">Y</span>.not(<span style="color:rgb(0,0,255)">4</span>)).forEach(System.<span style="color:rgb(25,72,166);font-weight:bold;font-style:italic">out</span>::println);<br></pre></div><div>Of course, nobody will do this but it looks nice to me.</div><div><br></div><div>With best regards,</div><div>Tagir Valeev</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Mar 17, 2020 at 4:58 PM Remi Forax <<a href="mailto:forax@univ-mlv.fr">forax@univ-mlv.fr</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><div style="font-family:arial,helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><div>Hi Daniel,<br></div><div>Pooling of Java objects in memory usually do more harm than good, because you artificially change the liveness of the pooled objects which doesn't work well with modern GC algorithms.</div><div>Obviously, if objects are stored in a database, it can still be a win but usually the pool is more or less coupled with the ORM and use off-heap memory.<br></div><div><br></div><div>EnumSet/EnumMap are specialized because Enum.ordinal() is a perfect hashcode function, so you can implement fast and compact set and map. It's not clear to me why there is a need for a specialized version of set/map for record given has you said a record is a class. Do we miss something ?<br></div><div><br></div><div>About filtering fields value on stream, adding a method isEqual that takes a mapping function and a value should be enough,</div><div>  Predicate<Point> filter = Predicate.isEqual(Company::name, "Apple");<br></div><div>I remember the lambda EG discuss that method (and its primitive variations), i don't remember why it's was not added.</div><div><br></div><div>About introducing a typesafe representation of fields, we currently provide a non-typesafe representation, j.l.r.RecordComponent.</div><div>There is no typesafe representation because what :: means on a field is still an open question.<br></div><div>And if we go that way, a static final field is not the best representation in term of classfile because it is initialized too early. A ldc constantdynamic + a static method or something along that line is a better idea. <br></div><div><br></div><div>regards,<br></div><div>Rémi<br></div><div><br></div><hr id="gmail-m_5466239737981046841zwchr"><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><b>De: </b>"Brian Goetz" <<a href="mailto:brian.goetz@oracle.com" target="_blank">brian.goetz@oracle.com</a>><br><b>À: </b>"amber-spec-experts" <<a href="mailto:amber-spec-experts@openjdk.java.net" target="_blank">amber-spec-experts@openjdk.java.net</a>><br><b>Envoyé: </b>Lundi 16 Mars 2020 21:24:04<br><b>Objet: </b>Fwd: [Records] Transparency and effects on collections<br></blockquote></div><div><blockquote style="border-left:2px solid rgb(16,16,255);margin-left:5px;padding-left:5px;color:rgb(0,0,0);font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt"><font size="+1"><tt>Received on the -comments list.</tt></font><br>
    <div><br>
      <br>
      -------- Forwarded Message --------
      <table cellspacing="0" cellpadding="0" border="0">
        <tbody>
          <tr>
            <th valign="BASELINE" nowrap align="RIGHT">Subject:
            </th>
            <td>[Records] Transparency and effects on collections</td>
          </tr>
          <tr>
            <th valign="BASELINE" nowrap align="RIGHT">Date: </th>
            <td>Wed, 11 Mar 2020 07:45:32 -0700 (PDT)</td>
          </tr>
          <tr>
            <th valign="BASELINE" nowrap align="RIGHT">From: </th>
            <td>Daniel Latrémolière
              <a href="mailto:daniel.latremoliere@gmail.com" target="_blank"><daniel.latremoliere@gmail.com></a><br></td>
          </tr>
          <tr>
            <th valign="BASELINE" nowrap align="RIGHT">To: </th>
            <td><a href="mailto:amber-spec-comments@openjdk.java.net" target="_blank">amber-spec-comments@openjdk.java.net</a><br></td>
          </tr>
        </tbody>
      </table>
      <br>
      <br>
      I understand that records are transparent and have correct
      equals/hashcode, then are useful as keys in collections. When
      trying to find classes to evolve to records, I found classes
      having more or less the same use-cases in memory than a
      multi-column primary key would have in SQL.<br>
------------------------------------------------------------------------<br>
      Records are a sugar above classes, like enum but for another use
      case, is it planned to have more evolved collections (like enum
      has with EnumSet/EnumMap)? Given records are explicitly
      transparent, API for pooling records would need to use this
      explicit transparency to allow partial queries and not only the
      Set/Map exact operations.<br>
      <br>
      If this is the case, it would probably need some specialised
      subtype of Set, like a new RecordSet (similar to a simple table
      without join, contrary to SQL). Current Java's Stream API would
      probably be perfect with some small enhancements, JPA-like (on a
      sub-type RecordStream<R>) allowing to refer directly to the
      field to be filtered.<br>
      <br>
      In this case, the compiler would need to generate for each record
      one static field per instance field of the record to allow typed
      queries, like in the following example.<br>
      <br>
      |record R(|||String foo, ...)| {||<br>
      ||   ...||<br>
      ||}||<br>
      |<br>
      desugarized more completely in:<br>
      <br>
      |class R {||<br>
      ||  public static final RecordField<R, String> FOO;||<br>
      ||  private String foo;||<br>
      ||   ...||<br>
      ||}||<br>
      |<br>
      It would allow some typed code for partial querying, like:<br>
      <br>
      |RecordSet<R> keyPool;||<br>
      ||Predicate<String> fooFilter;||<br>
      ||keyPool.stream().filter(R.FOO, fooFilter).forEach(...);||<br>
      |<br>
      <br>
      Thanks for your attention,<br>
      Daniel.<br>
------------------------------------------------------------------------<br>
      NB: in desugarization, I used standard static fields, like JPA,
      and not an enum containing all meta-fields (which would probably
      be more correct and efficient). This is due to the lack of JEP 301
      (needed for typed constants in enum). If allowed, the desugarized
      record will become something like:<br>
      <br>
      |class R {||<br>
      ||  public static enum META<X> implements RecordField<R,
      X> {||<br>
      ||    FOO<String>(String.class);||<br>
      ||  }||<br>
      ||  private String foo;||<br>
      ||  ...||<br>
      ||}||<br>
      |<br>
      In query, it would be used as R.META.FOO for filtering on field
      "foo" of the record:<br>
      <br>
      |keyPool.stream().filter(R.META.FOO, fooFilter).forEach(...)|<br>
------------------------------------------------------------------------<br>
      PS: I am not interested in interning records but using them in
      pools defined by programmer. Pooling would improve memory and
      performance to deduplicate records, because equals would more
      frequently succeed at identity test without continuing to real
      equality test (field by field). Having specialized implementations
      of collections, using fields of records following the order given
      by user, would probably be useful for performance against simple
      Set/Map: structures like a hierarchical Map of Map of ..., field
      by field, can be more efficient if partial queries are frequently
      used or if the pool is big.<br>
    </div><br></blockquote></div></div></div></blockquote></div>