Open leaxoy opened 1 year ago
There's already a set proposal that is snagged on iteration. This seems related to that. This could be containers/ordered.Set and containers/ordered.Map and have overlapping methods with containers/set.
- We aren't going to add any general purpose containers until we have an idea of how to implement iterators, aka how to find all the elements in the container. Iterators are pending resolution of user-defined iteration using range over func values #56413, which itself is pending the forward/backward compatibility work that will be part of Go 1.21.
- This should perhaps be a discussion first.
@ianlancetaylor I'm agree that Iter
is a important feature to iterate over all element in container as you explained, but there also many features does't relay on Iter
, such as: map.Get/Insert/Delete/Contains/Clear/IsEmpty/First/Last
and set.Insert(Many)/Contains(All/Any)/Delete/Clear/IsEmpty
. They are useful in many scenarios.
A benefit is map's Key and set's Elem can any type, not limited to
cmp.Ordered
.
For those internal maybe relay on Iter
, like set.SuperSetOf/SubSetOf/Difference/Union/InterSection
and map.Merge/Keys/Values
, not exposing implementation details can leave room for the introduction of Iter
later.
The rest are Iter
related, like: Iter
can be ecognized by the compiler and can be used in for-loop, and iterate operations can based on this, including but not limited to ForEach/Map/Filter/Reduce/Fold/Max/Min/AllOf/AnyOf/Take/Skip/Cmp/Zip/Unzip/Chunk/GroupBy
...
At least for CPP, operations on containers can be split into several parts, Iterators/Element Access/Capacity/Modifiers/List Operations/Lookup
.. at Containers, Iterators
works with std::ranges
.
If I understand correctly, what you care about above is the Iterators
part
I am curious why an ordered map cannot be introduced or considered. As stated, there are many different implementations of it, which are increasingly being adopted. Still, they are painful to implement when using third-party libraries (especially when those involve range
iterations).
Because many languages have already adopted a similar functionality (some examples):
To provide a more concrete example, I am using libopenapi, which uses a custom orderedmap, rendering specifications requires the ordering to be in place, especially when visualizing the results. With the current lack of an ordered map solution, a developer must introduce another solution compatible with the native supporting packages (e.g., templates and range functionality).
Coming back, with the use of yield
things become a bit easier. I gave yield spin today over an iteration of an ordered map that previously required me to convert to a slice and loop over the slice and then over the map.
I still believe that adding support for an ordered map/set makes sense.
The Keys and Values methods (and a new All method) should definitely use go1.23 iterators rather than materializing a slice.
func (*Map[K, V]) Keys() iter.Seq[K]
func (*Map[K, V]) Values() iter.Seq[V]
func (*Map[K, V]) All() iter.Seq2[K, V]
Reverse too could return an iterator rather than a clone:
func (*Map[K, V]) Reverse() iter.Seq2[K, V]
Similarly, SubMap (renamed Range) could be an iterator for a portion of the key space. If you need to construct a new map from a Range (or All), combine the iterator with a InsertAll method that consumes the iterator.
func (*Map[K, V]) Range(from, to K) iter.Seq2[K, V]
func (*Map[K, V]) InsertAll(iter.Seq2[K, V]) bool
What is Entry? Just a pair? Or a reference to a part of the representation? (Java's Map.Entry took the latter choice and IIRC it was a major barrier to optimized special-purpose maps, because they still had to allocate one object per entry.) Is it necessary?
I like that all the operations return the maximum amount of information in a single access (e.g. Insert reports whether there was a previous entry), as these can really improve efficiency.
Retain should be DeleteFunc, with sense inverted.
We should try to harmonize the APIs of related collections types such as sets, lists, maps, queues, and so on. Java 1.5's collections did a good job of establishing conventions across a range of data types so that, for example, they all have an add(T) bool
method that reports whether the size of the collection changed (though the API has grown to the point where I can no longer hold in memory).
Naming suggestion: package ordered; type Map; func NewMap{,Func}
? Or package maps; type Ordered; func NewOrdered{,Func}
?
I don't like Range
since it clashes with range
in a confusing way though I'm not sure what a better name is.
InsertAll
should just be Insert
because of maps.Insert
.
:+1: to container/ordered
. maps
is for native maps. If there's a type in maps
, it should be for wrapping a native map in something with all the usual methods for other container types.
Also, SuperSetOf
should be SupersetOf
.
I feel like container/ordered
is ever so mildly confusing because there are lot ordered container types that make no sense in it, such as just a regular linked list, though I see the appeal of ordered.Map[K, V]
.
@DeedleFake I would expect inserting into an ordered.List
to perform the insertion at an index that maintains sort order. So an ordered.Set
that allows duplicates.
The Keys and Values methods (and a new All method) should definitely use go1.23 iterators rather than materializing a slice.
func (*Map[K, V]) Keys() iter.Seq[K] func (*Map[K, V]) Values() iter.Seq[V] func (*Map[K, V]) All() iter.Seq2[K, V]
Reverse too could return an iterator rather than a clone:
func (*Map[K, V]) Reverse() iter.Seq2[K, V]
Similarly, SubMap (renamed Range) could be an iterator for a portion of the key space. If you need to construct a new map from a Range (or All), combine the iterator with a InsertAll method that consumes the iterator.
func (*Map[K, V]) Range(from, to K) iter.Seq2[K, V] func (*Map[K, V]) InsertAll(iter.Seq2[K, V]) bool
What is Entry? Just a pair? Or a reference to a part of the representation? (Java's Map.Entry took the latter choice and IIRC it was a major barrier to optimized special-purpose maps, because they still had to allocate one object per entry.) Is it necessary?
I like that all the operations return the maximum amount of information in a single access (e.g. Insert reports whether there was a previous entry), as these can really improve efficiency.
Retain should be DeleteFunc, with sense inverted.
We should try to harmonize the APIs of related collections types such as sets, lists, maps, queues, and so on. Java 1.5's collections did a good job of establishing conventions across a range of data types so that, for example, they all have an
add(T) bool
method that reports whether the size of the collection changed (though the API has grown to the point where I can no longer hold in memory).Naming suggestion:
package ordered; type Map; func NewMap{,Func}
? Orpackage maps; type Ordered; func NewOrdered{,Func}
?
@adonovan
Agreed, when this proposal was made, iterators had not yet been implemented. Now that they have been implemented, it makes sense to use iterator syntax.
The existence of Entry is a personal preference. Since seq2 has never been seen in other languages, tuple is used instead, but since go uses seq2, there is no better reason not to use it here.
Short names are indeed better than long names, changed in original proposal.
In my personal implementation it has indeed been the short name for some time
Although I still think seq2 is a less successful abstraction. ^_^
I don't like
Range
since it clashes withrange
in a confusing way though I'm not sure what a better name is.
InsertAll
should just beInsert
because ofmaps.Insert
.👍 to
container/ordered
.maps
is for native maps. If there's a type inmaps
, it should be for wrapping a native map in something with all the usual methods for other container types.Also,
SuperSetOf
should beSupersetOf
.
@jimmyfrasche
Since the native map supports direct insertion of a key-value pair at the syntactic level, but this does not work here. If you insert a sequence using insert, then I cannot think of any other suitable naming for inserting the key-value.
How about use InsertSeq
or InsertIter
instead.
InsertSeq()
fits well with slices.AppendSeq()
.
There are at least several thousand related implementations OrderedMap/OrderedSet, although we have generics, there are no related containers in the standard library, so people have to implement their own sorting containers.
So I propose add
ordered.Map/Set
intostd
, maybecontainer
package is a suitable place.The main API are: