----- Mail original -----
> De: "dfranken jdk" <dfranken....@gmail.com>
> À: "Remi Forax" <fo...@univ-mlv.fr>
> Cc: "Stuart Marks" <stuart.ma...@oracle.com>, "core-libs-dev" 
> <core-libs-dev@openjdk.java.net>
> Envoyé: Mardi 11 Mai 2021 08:45:15
> Objet: Re: [External] : Re: ReversibleCollection proposal

> Dear mr. Forax,

Hi Dave,

> 
> I understand your point of view. You don't like to have separate
> standalone interfaces for things that seem to belong in a hierarchy, is
> that correct? Now that I thought about it, I agree.

yes,

> 
> So if a reversable/ordered set is a specialized form of a reversable
> collection where reversable collection is a specialized form of a
> collection, it makes sense to add it into the hierarchy.
> 
> Are you proposing to just add methods to the root Collection interface,
> because it already has methods which may or may not be implemented or
> throw Exceptions by its implementations ?

yes, the idea is to add getFirst/getLast/removeFirst/removeLast and reversed on 
Collection as default methods.
Something like

interface Collection<E> {
  /**
   * throws NoSuchElementException if the collection is empty
   */
  public default E getFirst() {
    return iterator().next();
  }

  /**
   * throws NoSuchElementException if the collection is empty
   * throws UnsupportedOperationException if the collection is non mutable
   */
  public default E removeFirst() {
    var it = iterator();
    var element = it.next();
    it.remove();
    return element;
  }

  /**
   * throws NonOrderedCollectionException if the collection is non ordered
   */
  public default Collection<E> reversed() {
    throw new NonOrderedCollectionException();
  }

  /**
   * throws NoSuchElementException if the collection is empty
   * throws NonOrderedCollectionException if the collection is non ordered
   */
  public default E getLast() {
    return reversed().getFirst();
  }

  /**
   * throws NoSuchElementException if the collection is empty
   * throws UnsupportedOperationException if the collection is non mutable
   * throws NonOrderedCollectionException if the collection is non ordered
   */
  public default E removeLast() {
    return reversed().removeFirst();
  }
}


And adds an implementation of reversed(), getLast() and removeLast() on 
NavigableSet/LinkedHashSet/EnumSet/Deque and List.

And do the same thing for Map:

interface Map<K, V> {
  /**
   * throws NoSuchElementException if the map is empty
   */
  public default Map.Entry<K,V> getFirstEntry() {
    return entrySet().iterator().next();
  }

  /**
   * throws NoSuchElementException if the map is empty
   * throws UnsupportedOperationException if the map is non mutable
   */
  public default  Map.Entry<K,V> removeFirstEntry() {
    var it = entrySet().iterator();
    var element = it.next();
    it.remove();
    return element;
  }

  /**
   * throws NonOrderedCollectionException if the map.keySet() is non ordered
   */
  public default Map<K,V> reversedMap() {
    throw new NonOrderedCollectionException();
  }

  /**
   * throws NoSuchElementException if the map is empty
   * throws NonOrderedCollectionException if the map.keySet() is non ordered
   */
  public default E getLastEntry() {
    return reversedMap().getFirstEntry();
  }

  /**
   * throws NoSuchElementException if the map is empty
   * throws UnsupportedOperationException if the map is non mutable
   * throws NonOrderedCollectionException if the map.keySet() is non ordered
   */
  public default E removeLast() {
    return reversedMap().removeFirstEntry();
  }
}

And adds an implementation of reversedMap(), getLastEntry() and 
removeLastEntry() on NavigableMap/LinkedHasMap and EnumMap.


> 
> But we could also view "having (possibly duplicate) items in a
> sequence" as a capability and that is defined by an actual interface,
> List.

It already exist for a Spliterator, the capability is called DISTINCT, by 
example, set.stream() and list.stream().distinct() are both Streams with no 
duplicate.

> 
> I do fear that if we add methods to the Collection interface, users
> might not expect them to throw exceptions. E.g. if we add getFirst(),
> users may just expect it to return the element that iterator().next()
> or .stream().findFirst().orElseThrow() would return instead of an
> UnsupportedOperationException. And while HashSet does not have a
> defined order, it would in practice always return the same element.

I agree, getFirst() and removeFirst() should have their semantics based on 
iterator (see above),
because as you said, it's already how findFirst() is specified.

> 
> So while I accept that some hidden capabilities are surfaced only
> through return values of certain methods, I think an interface is still
> a better choice here. However, if we go that route, I also agree on the
> fact that we might need to explore defining proper interfaces for the
> other capabilities at some point.
> 
> Kind regards,
> 
> Dave

regards,
Rémi

> 
> On Mon, 2021-05-10 at 12:22 +0200, fo...@univ-mlv.fr wrote:
>> ----- Mail original -----
>> > De: "dfranken jdk" <dfranken....@gmail.com>
>> > À: "Remi Forax" <fo...@univ-mlv.fr>, "Stuart Marks"
>> > <stuart.ma...@oracle.com>
>> > Cc: "core-libs-dev" <core-libs-dev@openjdk.java.net>
>> > Envoyé: Dimanche 9 Mai 2021 12:13:58
>> > Objet: Re: [External] : Re: ReversibleCollection proposal
>> 
>> > When I thought about Collection's role in the hierarchy, it seemed
>> > to
>> > me that 'Collection' is an interface for describing how elements
>> > are
>> > stored in a cardboard box (we can and and remove them) and that we
>> > might need a different, yet related, interface to describe how to
>> > retrieve the items from the box. This way we are not tied to the
>> > Collection hierarchy and we could have one Set implementation which
>> > is
>> > ordered and another Set implementation which is not and they would
>> > both
>> > still implement Collection, but only one would implement the
>> > interface.
>> 
>> So you want to model ReversibleSet as Set + Reversible,
>> Reversible being an interface with a small number of method specific
>> to the fact that the implementation is reversible.
>> 
>> This does not work well for two reasons,
>> - there is no syntax to represent the union of types in Java,
>>    Set & Reversible set = ...
>>   is not a valid syntax. You can use a type variable with several
>> bounds instead but it does work nicely with the rest of the language
>> (overidding, overloading, etc).
>> 
>> - having public methods that takes an interface with few methods is a
>> common design error.
>>   Let suppose you have a method foo that only prints the elements of
>> a collection, in that case you may want to type the first parameter
>> as Iterable or Collection.
>>   But requirements change an now you want to prints the elements
>> sorted, oops, you have to change the signature of the public method
>> which may be something not possible
>>   depending how many "clients" this method has.
>>   Providing interfaces with a small number of access methods will
>> lead to this kind of issue.
>> 
>> > 
>> > Imagine an interface like 'java.lang.OrderedEnumerable` if you will
>> > with methods such as 'first(), last(), etc', then each
>> > implementation
>> > would be free to choose to implement the interface or not. I also
>> > thought about 'OrderedIterable', but there would be too much
>> > confusion
>> > with 'Iterable', but I think these are related too. Retrieving
>> > items is
>> > an iteration problem, not a storage problem.
>> 
>> The problem is that is you multiply the number of interfaces to
>> access the elements, you add the dilemma of choice in the mix.
>> The first iteration of the Scala Collection were like this, too many
>> interfaces, at least for my taste.
>> 
>> > 
>> > While I would love to see the Collection hierarchy redesigned to
>> > also
>> > allow for ImmutableCollection which for instance would not have an
>> > `add(T e)` method, I don't think we can simply do something like
>> > that
>> > without breaking too many things.
>> 
>> Dear Santa, i want an interface BuilderCollection that only allows
>> add() but no remove()/clear(), because if you can not remove
>> elements, then all the iterators can implement the snapshot at the
>> beginning semantics,
>> so no ConcurrentModificationException anymore. For me, being able to
>> snapshot/freeze a collection is better than an ImmutableCollection,
>> because it can be immutable when you want.
>> 
>> Anyway, it's not gonna to happen, there is so many ways to slice an
>> onion and each have pros and cons and providing all the possible
>> interfaces is a good.way to make something simple complex.
>> 
>> > 
>> > So in short, separating retrieval aspects from storage aspects with
>> > a
>> > different interface might be the way to go.
>> > 
>> > Kind regards,
>> > 
>> > Dave Franken
>> > 
>> 
>> regards,
>> Rémi
>> 
>> > On Wed, 2021-05-05 at 12:41 +0200, fo...@univ-mlv.fr wrote:
>> > > ----- Mail original -----
>> > > > De: "Stuart Marks" <stuart.ma...@oracle.com>
>> > > > À: "Remi Forax" <fo...@univ-mlv.fr>
>> > > > Cc: "core-libs-dev" <core-libs-dev@openjdk.java.net>
>> > > > Envoyé: Mercredi 5 Mai 2021 02:00:03
>> > > > Objet: Re: [External] : Re: ReversibleCollection proposal
>> > > 
>> > > > On 5/1/21 5:57 AM, fo...@univ-mlv.fr wrote:
>> > > > > I suppose the performance issue comes from the fact that
>> > > > > traversing a
>> > > > > LinkedHahSet is slow because it's a linked list ?
>> > > > > 
>> > > > > You can replace a LinkedHashSet by a List + Set, the List
>> > > > > keeps
>> > > > > the values in
>> > > > > order, the Set avoids duplicates.
>> > > > > 
>> > > > > Using a Stream, it becomes
>> > > > >    Stream.of(getItemsFromSomeplace(),
>> > > > > getItemsFromAnotherPlace(),
>> > > > >    getItemsFromSomeplaceElse())
>> > > > >     .flatMap(List::stream)
>> > > > >     .distinct()              // use a Set internally
>> > > > >     .collect(toList());
>> > > > 
>> > > > The problem with any example is that simplifying assumptions
>> > > > are
>> > > > necessary for
>> > > > showing the example, but those assumptions enable it to be
>> > > > easily
>> > > > picked apart.
>> > > > Of
>> > > > course, the example isn't just a particular example; it is a
>> > > > *template* for a
>> > > > whole
>> > > > space of possible examples. Consider the possibility that the
>> > > > items
>> > > > processing
>> > > > client needs to do some intermediate processing on the first
>> > > > group
>> > > > of items
>> > > > before
>> > > > adding the other groups. This might not be possible to do using
>> > > > streams. Use
>> > > > your
>> > > > imagination.
>> > > > 
>> > > > > I think there are maybe some scenarios where
>> > > > > ReversibleCollection
>> > > > > can be useful,
>> > > > > but they are rare, to the point where when there is a good
>> > > > > scenario for it
>> > > > > people will not recognize it because ReversibleCollection
>> > > > > will
>> > > > > not be part of
>> > > > > their muscle memory.
>> > > > 
>> > > > I'm certain that uses of RC/RS will be rare in the beginning,
>> > > > because they will
>> > > > be
>> > > > new, and people won't be familar with them. And then there will
>> > > > the
>> > > > people who
>> > > > say
>> > > > "I can't use them because I'm stuck on JDK 11 / 8 / 7 / 6 ...."
>> > > > It
>> > > > was the same
>> > > > thing with lambdas and streams in Java 8, with List.of() etc in
>> > > > Java 9, records
>> > > > in
>> > > > Java 16, etc. This wasn't an argument not to add them, and it
>> > > > isn't
>> > > > an argument
>> > > > not
>> > > > to add RC/RS.
>> > > 
>> > > All the changes you are listing here are "client side" changes,
>> > > the
>> > > ones that can be adopted quickly because they do not require to
>> > > change the API side of any libraries.
>> > > ReversibleCollection is an API side change, like generics is,
>> > > those
>> > > changes have a far higher cost because you have to wait your
>> > > library
>> > > dependencies to be updated.
>> > > On the Valhalla list, we have discussed several times about how
>> > > to
>> > > alleviate those API side change cost using automatic bridging or
>> > > methods forwarding, even for Valhalla, we are currently moving in
>> > > a
>> > > state where those mechanisms are not needed.
>> > > 
>> > > > 
>> > > > > There is a real value to add methods like
>> > > > > descendingSet/descendingList()/getFirst/getLast on existing
>> > > > > collections, but we
>> > > > > don't need a new interface (or two) for that.
>> > > > 
>> > > > It depends on what you mean by "need". Sure, we could get away
>> > > > without this;
>> > > > after
>> > > > all, we've survived the past twenty years without it, so we
>> > > > could
>> > > > probably
>> > > > survive
>> > > > the next twenty years as well.
>> > > > 
>> > > > It would indeed be useful to add various methods to List,
>> > > > Deque,
>> > > > SortedSet, and
>> > > > LinkedHashSet to provide a full set of methods on all of them.
>> > > > And
>> > > > it would also
>> > > > be
>> > > > nice to have those methods be similar to one another. An
>> > > > interface
>> > > > helps with
>> > > > that,
>> > > > but I agree, that's not really the reason to have an interface
>> > > > though.
>> > > > 
>> > > > The reversed-view concept could also be added individually to
>> > > > the
>> > > > different
>> > > > places.
>> > > > A reverse-ordered List is a List, a reverse-ordered Deque is a
>> > > > Deque, a
>> > > > reverse-ordered SortedSet is a SortedSet, and a reverse-ordered
>> > > > LinkedHashSet is
>> > > > a
>> > > > ... ? And what is the type of the keySet of a LinkedHashMap,
>> > > > that
>> > > > enables one to
>> > > > (say) get the last element?
>> > > 
>> > > see below
>> > > 
>> > > > 
>> > > > After working with a system like this for a while, it begins to
>> > > > emerge that
>> > > > there is
>> > > > an abstraction missing from the collections framework,
>> > > > something
>> > > > like an
>> > > > "ordered
>> > > > collection". People have been missing this for quite a long
>> > > > time.
>> > > > The most
>> > > > recent
>> > > > example (which this proposal builds on) is Tagir's proposal
>> > > > from a
>> > > > year ago. And
>> > > > it's been asked about several times before that.
>> > > > ReversibleCollection fills in
>> > > > that
>> > > > missing abstraction.
>> > > 
>> > > The abstraction already exists but it's not defined in term of
>> > > interface because it's an implementation decision and those are
>> > > cleanly separated in the current Collection design.
>> > > 
>> > > Let take a step back, the collection API defines basic data
>> > > structure
>> > > operations in term of interfaces like List, Deque, Set, etc those
>> > > interfaces are decoupled from implementation capabilities like
>> > > mutable, nullable, ordered and checked.
>> > > 
>> > > Depending on the implementation capabilities, the interfaces
>> > > method
>> > > implementation may throw an exception, non-mutable
>> > > implementations
>> > > use UnsupportedOperationException, non-nullable implementations
>> > > use
>> > > NPE and checked implementations use CCE.
>> > > 
>> > > So what is missing is methods on Collection interfaces that
>> > > require
>> > > the collection implementation to be ordered like
>> > > descendingList(),
>> > > getFirst(), etc.
>> > > Those methods that may throw a specific exception if the
>> > > implementation is not ordered, not UnsupportedOperationException
>> > > but
>> > > a new one like NotOrderedException.
>> > > 
>> > > So to answer to your question about LinkedHashSet, the reverse-
>> > > ordered LinkedHashSet is a Set with a method descendingSet() that
>> > > do
>> > > not throw NotOrderedException like any Set with an order.
>> > > 
>> > > To summarize, if we introduce ReversibleCollection, we should
>> > > also
>> > > introduce ImmutableCollection, NonNullableCollection and
>> > > CheckedCollection.
>> > > I think it's better to consider the fact that being ordered as a
>> > > capability (hint: this is already what the Spliterator API does)
>> > > and
>> > > not as a specific interface.
>> > > 
>> > > > 
>> > > > s'marks
>> > > 
> > > > Rémi

Reply via email to