<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 26/08/2022 18:54, Ethan McCue wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:CA+NR86ifxrRcqYQ3UEYZgDaZPMnMiMgDD65-Ra3Um91Y_ZUaww@mail.gmail.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div dir="ltr">If the collections would decide whether or not to
        copy, I don't think just requesting an immutable reference would
        be enough. <br>
        <br>
            static <E> List<E> listCopy(Collection<?
        extends E> coll) {<br>
                if (coll instanceof List12 || (coll instanceof ListN
        && ! ((ListN<?>)coll).allowNulls)) {<br>
                    return (List<E>)coll;<br>
                } else {<br>
                    return (List<E>)List.of(coll.toArray()); //
        implicit nullcheck of coll<br>
                }<br>
            }<br>
        <br>
        The two things that List.copyOf needs to know are that the list
        is immutable, but also that it isn't a variant that might
        contain a null.<br>
      </div>
    </blockquote>
    <p>I really don't care about the null problem, that's a problem that
      the designers of this basically brought upon themselves, not
      because of any real inherit limitation that an immutable
      collection can't contain `null`. What irks even more is that the
      `List` interface provides no way to determine if an implementation
      is actively null hostile meaning that this code is no longer safe
      (or strictly, never really was safe due to rather weak guarantees
      made in the `List` interface):</p>
    <p>      List<?> aList = ... ; // a list from somewhere</p>
    <p>      if (aList.contains(null)) throw
      IllegalArgumentException();   // this is unsafe, and will cause a
      NPE depending on the list type<br>
    </p>
    <p>This unfortunate choice was never that visible, but since
      `List.of` it occurs more frequently in standard code, and
      highlights that a leniently specified interface is mostly a
      useless interface.</p>
    <p>So, I don't see the reason to jump through hoops to use the same
      type of `List` that `List.of` or `List.copyOf` returns.  All that
      is required is that an immutable list is returned, which can be as
      simple as:</p>
    <p>      return Collections.unmodifiableList(clone());</p>
    <p>Or:</p>
    <p>      return Collections.unmodifiableList(new
      ArrayList<>(this));</p>
    <p>Or if already wrapped in the immutable wrapper simply `return
      this`.<br>
    </p>
    <blockquote type="cite"
cite="mid:CA+NR86ifxrRcqYQ3UEYZgDaZPMnMiMgDD65-Ra3Um91Y_ZUaww@mail.gmail.com">
      <div dir="ltr"><br>
        So maybe instead of<br>
        <br>
             List<T> y = x.immutableCopy();<br>
        <br>
        It could be appropriate to use the spliterator approach and
        request a copy which has certain characteristics.<br>
        <br>
            static <E> List<E> listCopy(Collection<?
        extends E> coll) {<br>
                if (coll instanceof List<?> list) {<br>
                    return list.copyWhere(EnumSet.of(IMMUTABLE,
        DISALLOW_NULLS));<br>
                } else {<br>
                    return (List<E>)List.of(coll.toArray()); //
        implicit nullcheck of coll<br>
                }<br>
            }<br>
        <br>
        but that leaves open whether you would want to request the
        *presence* of capabilities or the *absence* of them.<br>
        <br>
        Maybe <br>
        <br>
            List.of().copyWhere();<br>
        <br>
        Could be defined to give a list where it is immutable and nulls
        aren't allowed. And then<br>
        <br>
           List.of(1, 2, 3).copyWhere(EnumSet.of(ADDABLE,
        NULLS_ALLOWED));<br>
        <br>
        gives you a mutable copy where nulls are allowed.<br>
        <br>
        This still does presume that making a copy if a capability isn't
        present is the only use of knowing the capabilities - which from
        the conversation so far isn't that unrealistic</div>
    </blockquote>
    <p>I fear there are too many possibilities here to cover all use
      cases one could think of: Appendable, Prependable, Insertable,
      Removable, Popable, HeadRemovable(?), Permutable, Replacable, just
      to name a few.  A copy to create a modifiable version seems
      sufficient, and a custom solution is probably in order if that
      would cause performance issues (like a wrapper around an actual
      list that only allows specific functionality, like implements
      Appendable<T>).</p>
    <p>Perhaps with a method (or constructor) of the form:</p>
    <p>      <T extends List<T> & Appendable<T>>
      void giveMeAnAppendableList(T appendable);<br>
    </p>
    <p>--John<br>
    </p>
    <blockquote type="cite"
cite="mid:CA+NR86ifxrRcqYQ3UEYZgDaZPMnMiMgDD65-Ra3Um91Y_ZUaww@mail.gmail.com"><br>
      <div class="gmail_quote">
        <div dir="ltr" class="gmail_attr">On Fri, Aug 26, 2022 at 11:20
          AM John Hendrikx <<a href="mailto:john.hendrikx@gmail.com"
            moz-do-not-send="true" class="moz-txt-link-freetext">john.hendrikx@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>
            <p><br>
            </p>
            <div>On 24/08/2022 15:38, Ethan McCue wrote:<br>
            </div>
            <blockquote type="cite">
              <div dir="auto">A use case that doesn't cover is adding to
                a collection.</div>
            </blockquote>
            <blockquote type="cite">
              <div dir="auto">
                <div dir="auto">Say as part of a method's contract you
                  state that you take ownership of a List. You aren't
                  going to copy even if the list is mutable.</div>
                <div dir="auto"><br>
                </div>
                <div dir="auto">Later on, you may want to add to the
                  list. Add is supported on ArrayList so you don't need
                  to copy and replace your reference, but you would if
                  the list you were given was made with List.of or
                  Arrays.asList</div>
              </div>
            </blockquote>
            <p>I don't think this is a common enough use case that
              should be catered for.  It might be better handled with
              concurrent lists instead.</p>
            <p>The most common use case by far is wanting to make sure a
              collection you've received is not going to be modified
              while you are working with it.  I don't think another
              proposal which does cover the most common cases should be
              dismissed out of hand because it doesn't support a rather
              rare use case.<br>
            </p>
            --John<br>
            <p><br>
            </p>
            <blockquote type="cite"><br>
              <div class="gmail_quote">
                <div dir="ltr" class="gmail_attr">On Wed, Aug 24, 2022,
                  8:13 AM John Hendrikx <<a
                    href="mailto:john.hendrikx@gmail.com"
                    target="_blank" moz-do-not-send="true"
                    class="moz-txt-link-freetext">john.hendrikx@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>
                    <p>Would it be an option to not make the receiver
                      responsible for the decision whether to make a
                      copy or not?  Instead put this burden (using
                      default methods) on the various collections?</p>
                    <p>If List/Set/Map had a method like this:</p>
                    <p>     List<T> immutableCopy();  // returns a
                      (shallow) immutable copy if list is mutable
                      (basically always copies, unless proven otherwise)</p>
                    <p>Paired with methods on Collections to prevent
                      collections from being modified:</p>
                    <p>     Collections.immutableList(List<T>)</p>
                    <p>This wrapper is similar to `unmodifiableList`
                      except it implements `immutableCopy` as `return
                      this`.<br>
                    </p>
                    <p>Then for the various scenario's, where `x` is an
                      untrusted source of List with unknown status:<br>
                    </p>
                    <p>     // Create a defensive copy; result is a
                      private list that cannot be modified:<br>
                    </p>
                    <p>     List<T> y = x.immutableCopy();   <br>
                    </p>
                    <p>     // Create a defensive copy for sharing,
                      promising it won't ever change:<br>
                    </p>
                    <p>      List<T> y =
                      Collections.immutableList(x.immutableCopy()); <br>
                    </p>
                    <p>     // Create a defensive copy for mutating:</p>
                    <p>     List<T> y = new ArrayList<>(x); 
                      // same as always</p>
                    <p>     // Create a mutable copy, modify it, then
                      expose as immutable:</p>
                    <p>     List<T> y = new ArrayList<>(x); 
                      // same as always</p>
                    <p>     y.add( <some element> );     <br>
                    </p>
                    <p>     List<T> z =
                      Collections.immutableList(y);</p>
                    <p>     y = null;  // we promise `z` won't change
                      again by clearing the only path to mutating it!<br>
                    </p>
                    <p>The advantage would be that this information
                      isn't part of the type system where it can easily
                      get lost. The actual implementation knows best
                      whether a copy must be made or not.</p>
                    <p>Of course, the immutableList wrapper can be used
                      incorrectly and the promise here can be broken by
                      keeping a reference to the original (mutable)
                      list, but I think that's an acceptable trade-off.</p>
                    <p>--John</p>
                    <p>PS. Chosen names are just for illustration; there
                      is some discussion as what "unmodifiable" vs
                      "immutable" means in the context of collections
                      that may contain elements that are mutable. In
                      this post, immutable refers to shallow
                      immutability .</p>
                    <div>On 24/08/2022 03:24, Ethan McCue wrote:<br>
                    </div>
                    <blockquote type="cite">
                      <div dir="auto">Ah, I'm an idiot.
                        <div dir="auto"><br>
                        </div>
                        <div dir="auto">There is still a proposal here
                          somewhere...maybe. right now non jdk lists
                          can't participate in the special casing?</div>
                      </div>
                      <br>
                      <div class="gmail_quote">
                        <div dir="ltr" class="gmail_attr">On Tue, Aug
                          23, 2022, 9:00 PM Paul Sandoz <<a
                            href="mailto:paul.sandoz@oracle.com"
                            rel="noreferrer" target="_blank"
                            moz-do-not-send="true"
                            class="moz-txt-link-freetext">paul.sandoz@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">List.copyOf
                          already does what you want.<br>
                          <br>
                          <a
href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068"
                            rel="noreferrer noreferrer noreferrer"
                            target="_blank" moz-do-not-send="true"
                            class="moz-txt-link-freetext">https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/List.java#L1068</a><br>
                          <a
href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168"
                            rel="noreferrer noreferrer noreferrer"
                            target="_blank" moz-do-not-send="true"
                            class="moz-txt-link-freetext">https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ImmutableCollections.java#L168</a><br>
                          <br>
                          Paul.<br>
                          <br>
                          > On Aug 23, 2022, at 4:49 PM, Ethan McCue
                          <<a href="mailto:ethan@mccue.dev"
                            rel="noreferrer noreferrer" target="_blank"
                            moz-do-not-send="true"
                            class="moz-txt-link-freetext">ethan@mccue.dev</a>>
                          wrote:<br>
                          > <br>
                          > Hi all,<br>
                          > <br>
                          > I am running into an issue with the
                          collections framework where I have to choose
                          between good semantics for users and
                          performance.<br>
                          > <br>
                          > Specifically I am taking a java.util.List
                          from my users and I need to choose to either<br>
                          > * Not defensively copy and expose a
                          potential footgun when I pass that List to
                          another thread<br>
                          > * Defensively copy and make my users pay
                          an unnecessary runtime cost.<br>
                          > <br>
                          > What I would really want, in a nutshell,
                          is for List.copyOf to be a no-op when used on
                          lists made with List.of().<br>
                          > <br>
                          > Below the line is a pitch I wrote up on
                          reddit 7 months ago for a mechanism I think
                          could accomplish that. My goal is to share the
                          idea a bit more widely and to this specific
                          audience to get feedback.<br>
                          > <br>
                          > <a
href="https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3"
                            rel="noreferrer noreferrer noreferrer"
                            target="_blank" moz-do-not-send="true">https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3</a>
                          <br>
                          > <br>
                          > Important also for context is Ron
                          Pressler's comment above.<br>
                          > --------------<br>
                          > <br>
                          > What if the collections api added more
                          marker interfaces like RandomAccess?<br>
                          > <br>
                          > It's already a common thing for codebases
                          to make explicit null checks at error
                          boundaries because the type system can't
                          encode null | List<String>. <br>
                          > <br>
                          > This feels like a similar problem.<br>
                          > If you have a List<T> in the type
                          system then you don't know for sure you can
                          call any methods on it until you check that
                          its not null. In the same way, there is a set
                          of methods that you don't know at the
                          type/interface level if you are allowed to
                          call.<br>
                          > <br>
                          > If the List is actually a __<br>
                          > Then you can definitely call<br>
                          > And you know other reference holders
                          might call<br>
                          > And you can confirm its this case by<br>
                          > null<br>
                          > no methods<br>
                          > no methods<br>
                          > list == null<br>
                          > List.of(...)<br>
                          > get, size<br>
                          > get, size<br>
                          > ???<br>
                          > Collections.unmodifiableList(...)<br>
                          > get, size<br>
                          > get, size, add, set<br>
                          > ???<br>
                          > Arrays.asList(...)<br>
                          > get, size, set<br>
                          > get, size, set<br>
                          > ???<br>
                          > new ArrayList<>()<br>
                          > get, size, add, set<br>
                          > get, size, add, set<br>
                          > ???<br>
                          > While yes, there is no feasible way to
                          encode these things in the type system. Its
                          not impossible to encode it at runtime though.<br>
                          > interface FullyImmutable {<br>
                          > // So you know the existence of this
                          implies the absence<br>
                          > // of the others<br>
                          > default Void cantIntersect() { return
                          null; }<br>
                          > }<br>
                          > <br>
                          > interace MutationCapability {<br>
                          > default String cantIntersect() { return
                          ""; }<br>
                          > }<br>
                          > <br>
                          > interface Addable extends
                          MutationCapability {}<br>
                          > interface Settable extends
                          MutationCapability {}<br>
                          > <br>
                          > If the List is actually a __<br>
                          > Then you can definitely call<br>
                          > And you know other reference holders
                          might call<br>
                          > And you can confirm its this case by<br>
                          > null<br>
                          > no methods<br>
                          > no methods<br>
                          > list == null<br>
                          > List.of(...)<br>
                          > get, size<br>
                          > get, size<br>
                          > instanceof FullyImmutable<br>
                          > Collections.unmodifiableList(...)<br>
                          > get, size<br>
                          > get, size, add, set<br>
                          > !(instanceof Addable) &&
                          !(instanceof Settable)<br>
                          > Arrays.asList(...)<br>
                          > get, size, set<br>
                          > get, size, set<br>
                          > instanceof Settable<br>
                          > new ArrayList<>()<br>
                          > get, size, add, set<br>
                          > get, size, add, set<br>
                          > instanceof Settable && instanceof
                          Addable<br>
                          > In the same way a RandomAccess check
                          let's implementations decide whether they want
                          to try an alternative algorithm or crash, some
                          marker "capability" interfaces would let users
                          of a collection decide if they want to clone
                          what they are given before working on it.<br>
                          > <br>
                          > <br>
                          > --------------<br>
                          > <br>
                          > So the applicability of this would be
                          that the list returned by List.of could
                          implement FullyImmutable, signifying that
                          there is no caller which might have a mutable
                          handle on the collection. Then List.of could
                          check for this interface and skip a copy.<br>
                          > <br>
                          > <br>
                          <br>
                        </blockquote>
                      </div>
                    </blockquote>
                  </div>
                </blockquote>
              </div>
            </blockquote>
          </div>
        </blockquote>
      </div>
    </blockquote>
  </body>
</html>