slices: new package to provide generic slice functions

Note: Discussion is now at https://github.com/golang/go/discussions/47203.
This proposal is for use with #43651. We propose defining a new package, slices, that will provide functions that may be used with slices of any type.  If this proposal is accepted, the new package will be included with the first release of Go that implements #43651 (we currently expect that that will be Go 1.18).
This description below is focused on the API, not the implementation. In general the implementation will be straightforward.  The description has been updated from the original proposal several times based on the discussion.
// Package slices defines various functions useful with slices of any type.
// Unless otherwise specified, these functions all apply to the elements
// of a slice at index 0 <= i < len(s).
package slices

import "constraints" // See #45458

// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[T comparable](s1, s2 []T) bool

// EqualFunc reports whether two slices are equal using a comparison
// function on each pair of elements. If the lengths are different,
// EqualFunc returns false. Otherwise, the elements are compared in
// index order, and the comparison stops at the first index for which
// eq returns false.
func EqualFunc[T1, T2 any](s1 []T1, s2 []T2, eq func(T1, T2) bool) bool

// Compare compares the elements of s1 and s2.
// The elements are compared sequentially starting at index 0,
// until one element is not equal to the other. The result of comparing
// the first non-matching elements is the result of the comparison.
// If both slices are equal until one of them ends, the shorter slice is
// considered less than the longer one
// The result will be 0 if s1==s2, -1 if s1 < s2, and +1 if s1 > s2.
func Compare[T constraints.Ordered](s1, s2 []T) int

// CompareFunc is like Compare, but uses a comparison function
// on each pair of elements. The elements are compared in index order,
// and the comparisons stop after the first time cmp returns non-zero.
// The result will be the first non-zero result of cmp; if cmp always
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
// and +1 if len(s1) > len(s2).
func CompareFunc[T any](s1, s2 []T, cmp func(T, T) int) int

// Index returns the index of the first occurrence of v in s, or -1 if not present.
func Index[T comparable](s []T, v T) int

// IndexFunc returns the index into s of the first element
// satisfying f(c), or -1 if none do.
func IndexFunc[T any](s []T, f func(T) bool) int

// Contains reports whether v is present in s.
func Contains[T comparable](s []T, v T) bool

// Insert inserts the values v... into s at index i, returning the modified slice.
// In the returned slice r, r[i] == the first v.  Insert panics if i is out of range.
// This function is O(len(s) + len(v)).
func Insert[S constraints.Slice[T], T any](s S, i int, v ...T) S

// Delete removes the elements s[i:j] from s, returning the modified slice.
// Delete panics if s[i:j] is not a valid slice of s.
// Delete modifies the contents of the slice s; it does not create a new slice.
// Delete is O(len(s)-(j-i)), so if many items must be deleted, it is better to
// make a single call deleting them all together than to delete one at a time.
func Delete[S constraints.Slice[T], T any](s S, i, j int) S

// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
func Clone[S constraints.Slice[T], T any](s S) S

// Compact replaces consecutive runs of equal elements with a single copy.
// This is like the uniq command found on Unix.
// Compact modifies the contents of the slice s; it does not create a new slice.
func Compact[S constraints.Slice[T], T comparable](s S) S

// CompactFunc is like Compact, but uses a comparison function.
func CompactFunc[S constraints.Slice[T], T any](s S, cmp func(T, T) bool) S

// Grow grows the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow will panic.
func Grow[S constraints.Slice[T], T any](s S, n int) S

// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
func Clip[S constraints.Slice[T], T any](s S) S

Remove and RemoveSlice are presumably in place?
Other in-place operations have InPlace in the name, but that's to distinguish them from the non-in-place versions.
Not sure if adding InPlace to Remove and RemoveSlice is worth it. But mentioning in-place-ness in the docs wouldn't hurt.

// Cut removes j-i elements from s starting at index i, returning the modified slice.
...
func RemoveSlice[T any](s []T, i, j int) []T

Did you intend for this to be Cut or RemoveSlice?

In general I think a slices package will be necessary. Functions like Equal, Insert, and Remove will be especially welcome improvements over the current boilerplate.
I have two concerns about the list of functions here:

Too much bloat to start with. I think we should err on the side of being too minimal and add new functions in future releases as they prove worthwhile. For instance, I think that CompareFunc will be rarely used and doesn't obviously pull its weight here.

 My experience with languages like JS and Java (and quite a few others which were more C-like in their initial incarnations and added a lot of higher-order function stuff later) is that there is an unfortunate dichotomy between writing for loops and using higher-order functions.
Code which heavily uses the latter looks very different on the page from the former, impeding readability.
The higher-order function style also tends to be much slower, so when writing a loop there's a decision burden every time between saving a line or two and writing fast code. (TIMTOWTDI is not really the Go ethos.) And programmers usually prefer concision, so the net result is often slow, HOF-heavy code.
The HOF style of code is also a bit clunky with Go's current function literals; adding many more HOFs will create pressure to add new syntax (#21498).
In summary, I feel like this nudges the language in a non-Go-like direction. A thing that I really like about Go is that, for many tasks, I just write a plain, clear for loop which gets compiled to the obvious machine code. I don't look forward to a future where the prevailing style is to replace each for loop with a sufficiently clever slices.Reduce.

With these ideas in mind, I'd divide this list of functions into three groups.
Clearly worthwhile in a v1 of slices:

Equal
Compare
Index
Contains
LastIndex
Insert
InsertSlice
Remove
RemoveSlice
Resize

Borderline:

EqualFunc
ContainsFunc
Map
Filter
FilterInPlace

Not worth it:

CompareFunc
IndexFunc
LastIndexFunc
Reduce
MapInPlace

@cespare I meant RemoveSlice.  Thanks.  Updated.

@randall77 Thanks, updated the comments.

AW

Why not container/slices and later if needed container/maps ?

Should this new package mention https://github.com/golang/go/wiki/SliceTricks?

Also, Index has in-band error signalling, which is strange? Isn't it less surprising to return (i int, exists bool), to mirror maps?

@sfllaw see https://golang.org/pkg/strings/#Index, bytes.Index, etc.

Hmm, good point!

@ianlancetaylor MapInPlace[type D, S] uses the old syntax. It should be MapInPlace[D, S any].

@ianlancetaylor did you mean?;
-func MapInPlace[type D, S](dst []D, src []S, f func(S) D)
+func MapInPlace[D, S](dst []D, src []S, f func(S) D)

One think to point out is that the Map and MapInPlace could be unified under MapAppend[D, S any](dst []D, src []S, f func(S) D), which appends to dst - Map would be equivalent to passing nil for dst and MapInPlace would be passing dst[:0]. Similar for Filter. Not saying we should, just that we could. And I find InPlace a slightly weird bikeshed color for Map, where D and S can be different, thus the mapping is never "in place".
Personally, I'm a bit concerned about Index and especially Contains. IMO they suggest that unordered slices are a suitable data structure for sets. Anecdotally, using x in y for a Python list was the single most common mistake I had to deduct points for.
I'd be less concerned if there was the equivalent thing for sorted slices as well, so that people at least be made to think about it. Though that would probably live in sort.

Would flatten and flatMap also be included?
I can think of cases where a map would produce a slice of slices and that might need flattening

I think this is a good idea, and it opens the door to including more functions by default in the future. Functions like these are going to either be provided by the standard library, or we'll end up with multiple "third party" libraries which do these things all in their own way.
Providing this out of the box would be in-line with how Go provides other frequently used constructs natively (like handling json data).

// Resize returns s with length c. If the length increases, the new trailing
// elements will be set to the zero value of T.  The capacity of the returned
// slice is not specified.
func Resize[T any](s []T, c int) []T

Not obvious what would be the behavior in case where len(s) > c. Would the returning slice be a truncated s, taking the first c elements or will s be returned as-is?
// Map turns a []T1 to a []T2 using a mapping function.
func Map[T1, T2 any](s []T1, f func(T1) T2) []T2

// Filter returns a new slice containing all elements e in s for which keep(e) is true.
func Filter[T any](s []T, keep func(T) bool) []T

// MapInPlace copies the elements in src to dst, using a mapping function
// to convert their values. This panics if len(dst) < len(src).
// (Or we could return min(len(dst), len(src)) if that seems better.)
func MapInPlace[type D, S](dst []D, src []S, f func(S) D)

// FilterInPlace modifies s to contain only elements for which keep(e) is true.
// It returns the modified slice.
func FilterInPlace[T any](s []T, keep func(T) bool) []T

I wonder it's might worth to provide some interface that would return a lazily-evaluated value here? As the chaining of Map and Filter is very common in Java Stream API.
So I wonder if we gona need a separate CL for something like Promise[T any].
// Insert inserts v into s at index i, returning the modified slice.
// In the returned slice r, r[i] == v.  This panics if !(i >= 0 && i <= len(s)).
// This function is O(len(s)).
func Insert[T any](s []T, i int, v T) []T

// InsertSlice inserts si into s at index i, returning the modified slice.
// In the returned slice r, r[i] == si[0] (unless si is empty).
// This panics if !(i >= 0 && i <= len(s)).
func InsertSlice[T any](s, si []T, i int) []T

// Remove removes the element at index i from s, returning the modified slice.
// This panics if !(i >= 0 && i < len(s)).  This function is O(len(s)).
// This modifies the contents of the slice s; it does not create a new slice.
func Remove[T any](s []T, i int) []T

// RemoveSlice removes j-i elements from s starting at index i, returning the modified slice.
// This can be thought of as a reverse slice expression: it removes a subslice.
// This panics if i < 0 || j < i || j > len(s).
// This modifies the contents of the slice s; it does not create a new slice.
func RemoveSlice[T any](s []T, i, j int) []T

I don't agree with usage of panic here.
Perhaps returning an err or there should be an Optional type/struct that can be either a value or an error.
Might be worth implementing these in a separate CL instead of the initial CL.

These seem like they would make our lives slightly easier, but I thought one of the philosophies of Go was that operations not in O(1) runtime shouldn't hide their complexity? I thought that was the main reason there were no simple commonly used abstractions like map, filter, etc in Go.
Although I suppose the runtime of these functions are quite universally understood by most developers...

One slice operation I've found myself writing frequently is “return the same slice with its cap reduced to length”.
#38081 was declined partially on the grounds that it could be written more clearly as a generic function (https://github.com/golang/go/issues/38081#issuecomment-610628025). I think such a function belongs in the slices package.

ContainsFunc, EqualFunc, CompareFunc feel likes odd-ones out right now, as they're nouns instead of verbs. Couldn't tell you what would be most go-ey, but e.g. ContainsByFunc might follow better purely on grammar

I agree with @cespare that some of the functional APIs here seem premature, especially given the lack of a concise lambda.
I would add that functional languages tend to rely heavily on covariance, which Go lacks. That makes functions like Reduce and even Map much less useful than the corresponding functions in functional programming languages. (I looked at Map in particular https://github.com/bcmills/go2go/blob/master/map.go2, but found it quite awkward compared to the typical functional map.)
On the other hand, Map and Reduce would be much more clearly useful if we had some way to express “assignable to” as a constraint, which I believe would be a coherent addition to the existing design. It's not obvious to me changing those functions to use such a constraint would be backward-compatible, so I think they should be omitted until we have more hands-on experience with generic functional programming in Go.

If you're going to do map/filter/reduce doesn't it make more sense to design a more generic interface, and the implement it for slices?  There are a lot more datastructures than slices that could benefit from such a thing.

I agree with @sluongng that the behavior of Resize seems unclear. I also don't really understand its purpose — can't we already resize a slice using the built-in slice operator?
I think a Clone method that returns a copy of a slice (up to its length) without accepting a length parameter — analogous to the bytes.Clone proposed in #45038 — would be much clearer for decreasing a length.
For increasing a length, I wonder if a function that increases the capacity rather than the length would be clearer, analogous to (*bytes.Buffer).Grow:
// Grow returns s with capacity at least c.
// If cap(s) >= c already, Grow returns s as-is.
func Grow[T any](s []T, c int) []T

That might also address @mdempsky's use case in proposal #24204 (see https://github.com/golang/go/issues/24204#issuecomment-738871874), since “allocate a new []T with capacity at least c” could be written as:
	s := slices.Grow[T](nil, c)

Would this be a bad time to suggest first class support for methods on primitive types? Obviously, users would not be able to define their own methods for primitives, and that would be fine, but the standard library could do this.
someSlice := []string{"foo", "bar"}
if someSlice.Contains("foo") {
    // ...
}

When I learned Go, it was extremely confusing to me that some things would have methods, and other things would require you to call a bare function in some package. I'm a little embarrassed at how long it took me to work out that it was the difference between primitives and "proper" types (because apparently primitives aren't good enough types to allow them to have methods?).
I personally think this is one of the bigger papercuts in the Go language. It's still a papercut, though, and not a bigger issue, because no one is getting seriously hurt by it, and you eventually learn to go with the flow... but I would be ecstatic to see primitives gain standard library methods. (Fundamental change often seems unpopular in any community, so I don't expect this idea to be widely embraced... generics wasn't embraced for a very long time.)

The code I was writing yesterday would have benefited from the existence of slices.Grow.
I am also strongly in favor of the Append idiom rather than InPlace. Append is already used throughout the standard library, whereas InPlace has no current uses.

one thing I have found useful on more than one occasion from JS's Reduce implementation is including the index as an argument to the provided function.
so it would be:
func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1, i int) T2) T2

The Slice variants of Insert seem like too much duplication to me. We have only one append in the language, not separate append variants for one element and multiple elements — why should Insert be any different?
If we make Insert analogous to append, that gives the signature:
func Insert[T any](s []T, i int, v ...T) []T

which seems like it handily covers the use-cases for both the proposed Insert and InsertSlice.

I think the signature for RemoveSlice may be surprising. I would intuitively expect it to accept a length instead of a second index.
That being the case, I think a function named RemoveN that explicitly accepts a length would be clearer — it's a bit more awkward for the “two indices” case, but it would be much more natural for many cases and also a lot less likely to be misread:
// RemoveN removes n elements from s starting at index i, returning the modified slice.
// This panics if i < 0 || n < 0 || i+n > len(s).
// This modifies the contents of the slice s; it does not create a new slice.
func RemoveN[T any][s []T, i, n int) []T

(The N suffix already has a precedent in the standard library in strings.SplitN.)

It may be desirable for the standard library to offer a convention around simple error handling.
With the proposed package, the programmer can map an []int to a []string: Map(in, strconv.Itoa). But they cannot map a []string to a []int. The programmer to write a wrapper struct{ res T1; err error } and adapting any f to one that uses the wrapper, but that is tedious and inefficient.
I think a common usage would benefit from:
// MapErr turns a []T1 to a []T2 using a mapping function.
// If an error is encountered during mapping, mapping stops and the first error is returned.
func MapErr[T1, T2 any](s []T1, f func(T1) (T2, error)) ([]T2, error)

This puts some pressure on also defining the execution order / concurrency, but I imagine the standard slices library would not attempt to process anything concurrently here.
I'm not sure about the spelling and how far this should be taken (Filter benefits from the same treatment, but what about the others?). But if we do nothing, people will end up writing their own generic Map functions, which echoes the situation around custom set libraries.

I can see the use case for MapErr, but when would you need FilterErr? You have a function that returns bool, error—why? ISTM more likely that you might need a trivial return err == nil wrapper for the filter, but even that's a little weird. I can't recall having written any code like that with a for-loop.

That said, I wonder if something more analogous to strings.Replace could cover the more advanced use-cases of both Remove and Insert:
// Replace replaces the elements at s[i:j] with the elements of si.
// This modifies the contents of the slice s; it does not create a new slice.
func Replace[T any](s []T, i, j int, si []T)

Then:
	w = slices.Insert(w, i, v)
	x = slices.InsertSlice(x, xs, i)
	y = slices.Remove(y, i)
	z = slices.RemoveSlice(z, i, j)

could all be expressed as:
	w = slices.Replace(w, i, i, []T{v})
	x = slices.Replace(x, i, i, xs)
	y = slices.Replace(y, i, i+1, nil)
	z = slices.Replace(z, i, j, nil)

@gaal, I think functions that are not pure transforms would be more clearly expressed as part of a general-purpose iterator library, rather than trying to make the slices library interact with things other than slices proper.
(I have a draft of such a package, written under the Go license, in https://github.com/bcmills/go2go/blob/master/iterate/iterate.go2.)

Instead of slices.Replace, you could use slices.Concat:
	w = slices.Concat(w[:i], []T{v}, w[i+1:])
	x = slices.Concat(x[:i], xs, x[i:])
	y = slices.Concat(y[:i], y[i+1:])
	z = slices.Replace(z[:i], z[j:])

Oh, my example concatenations are all allocating a new slice but you could have slices.AppendConcat if you wanted them to work in place.

some thoughts:

about Reduce, I expect this one will proceed the all slice. Then I'll miss First, Any and such to only return an element from the slice that passed the processing chain, but break
Doing so, If I have myHuuuugeSlice.Map(x->y).Filter(..).Any() I don't want it to process all elements in the slice, map them to y and filter, then eventually pick up any element: I want a processing pipeline to "pull" data from the "Any" collecter, and get previous step to provide "next" values (i.e. Iterator)

Apart from the valid discussion of whether they even pull their own weight at all, IndexFunc() and ContainsFunc() strike me as having an extra argument and possibly being redundant, given that go supports closures. The "static element" being checked against could easily just be captured by a closure, which also generalizes the function for other uses, like checking any other arbitrary property of the slice member.
Additionally, I wonder whether it's worth having an extra explicit "Sum" method. It seems likely that a significant fraction of Reduce usages might be summation, and I'd much rather read go code where that is explicit.

@coder543

When I learned Go, it was extremely confusing to me that some things would have methods, and other things would require you to call a bare function in some package. I'm a little embarrassed at how long it took me to work out that it was the difference between primitives and "proper" types (because apparently primitives aren't good enough types to allow them to have methods?).

Not quite. The rule is simply that when you define a method, the receiver type has to be defined in the same package. This necessarily excludes types that are defined in the language spec.
https://golang.org/ref/spec#Method_declarations

I think this package is inevitable and valuable.   Having an official package would be great but honestly the basics are simple and these would eliminate a lot of the one off implementations that were previously required.

I don't think Reduce should be marked as not worth it. Reduce is the primitive behind many other transformations.
For example, you can implement Map and Filter on top of Reduce:

func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1) T2) T2
	for _, i := range s {
		initializer = f(initializer, i)
	}
	return initializer
}

func Map[T1, T2 any](s []T1, f func(T1) T2) []T2
	return Reduce(s, make([]T2, 0), func(acc []T2, x T1) []T2 {
		return append(acc, f(x))
	})
}

func Filter[T any](s []T, keep func(T) bool) []T
	return Reduce(s, make([]T, 0), func(acc []T, x T) []T {
		if keep(x) {
			return append(acc, x)
		}
		return acc
	})
}

P.S.: I haven't tested the code above but it's purpose is just illustrative, so that should be fine :)

Thinking about what pieces to put in or not, I think the goals should be to have either something simple and common or something very flexible that covers all cases. There's no point in having something simple but covering an uncommon need. (Incidentally, performance shouldn't be a driving consideration on what to keep, since if you really need performance, you're going to write a for loop.)
With that in mind, func Equal[T comparable](s1, s2 []T) bool is simple and common. Good idea; keep it.
But EqualFunc and Compare don't really pull weight. How often do you really need them? If you're going to have anything, have CompareFunc, since it is flexible enough to cover all use cases. And if you don't have plain Compare, you can rename CompareFunc to Compare.
The same logic applies to Contains/Index.
So here are my picks:
// Equal reports whether two slices are equal: the same length and all
// elements equal. Floating point NaNs are not considered equal.
// The elements are compared in index order, and the comparison
// stops at the first unequal pair.
func Equal[T comparable](s1, s2 []T) bool

// Compare uses a comparison function
// on each pair of elements. The elements are compared in index order,
// and the comparisons stop after the first time cmp returns non-zero.
// The result will be the first non-zero result of cmp; if cmp always
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
// and +1 if len(s1) > len(s2).
func Compare[T any](s1, s2 []T, cmp func(T, T) int) int

// Index uses a comparison function to return the index of the first occurrence of v in s, or -1 if not present.
func Index[T any](s []T, v T, cmp func(T, T) bool) int

// Index uses a comparison function to return the index of the last occurrence of v in s, or -1 if not present.
func LastIndex[T any](s []T, v T, cmp func(T, T) bool) int

// Contains reports whether v is present in s.
func Contains[T comparable](s []T, v T) bool

// Map turns a []T1 to a []T2 using a mapping function.
func Map[T1, T2 any](s []T1, f func(T1) T2) []T2

func AppendMap[T1, T2 any](dst []T2, s []T1, f func(T1) T2) []T2

// Filter returns a new slice containing all elements e in s for which keep(e) is true.
func Filter[T any](s []T, keep func(T) bool) []T

func AppendFilter[T any](dst, s []T, keep func(T) bool) []T

// Insert inserts v into s at index i, returning the modified slice.
// In the returned slice r, r[i] == v.  This panics if !(i >= 0 && i <= len(s)).
// This function is O(len(s)).
func Insert[T any](s []T, i int, v T) []T

func Concat[T any](slices ...[]T) []T

func AppendConcat[T any](dst []T, slices ...[]T) []T

func Clone[T any](s []T) []T

func Grow[T any](s []T, n int) []T

func Reverse[T any](s []T) []T

func AppendReverse[T any](dst, s []T) []T

@coder543

When I learned Go, it was extremely confusing to me that some things would have methods, and other things would require you to call a bare function in some package. I'm a little embarrassed at how long it took me to work out that it was the difference between primitives and "proper" types (because apparently primitives aren't good enough types to allow them to have methods?).

Not quite. The rule is simply that when you define a method, the receiver type has to be defined in the same package. This necessarily excludes types that are defined in the language spec.
https://golang.org/ref/spec#Method_declarations

I deleted my comment several minutes before you replied because it was clear no one was interested in discussing this topic.
Your comment is a tautological response. Because they can't have methods, they can't have methods? We simply don't have the technology or know-how to change this as a language community? I disagree. This isn't one of the Hard Problems of computer science. The types have to be defined somewhere, and the methods could be defined there as well. If the methods have to be in the language spec, so be it! But it's not some impossible task. The year is 2021, and lots of languages have found a way to do this. It's purely a question of whether the community wants to do it. There is already the builtin/builtin.go file in the Go source tree that provides "documentation" for the various primitives. It would be conceptually trivial to simply say that this builtin package is where these types are defined, and therefore methods could be added in that location.
I'll probably delete this response too, because I'm not currently willing to fight for this papercut against the collective will of everyone else who is commenting here. Maybe I'll make a proper proposal issue some day when I have more energy to deal with this, but things will be harder by then because this proposal will have been accepted and implemented, adding even more inertia to this problem.
It's easy for people who are used to something to believe that the current way is naturally the best way, but just like generics were a good enough idea to be accepted by Go eventually, I think that primitives will eventually earn the right to have methods too.
The benefits of defining methods on primitives are enormous! Now people will be able to use Go Interfaces to accept primitive values! Didn't we just spend months bikeshedding syntaxes to work around this issue for Go generics?  If they could have methods, the built-in operators for those primitive types could be defined to also have corresponding methods like Equals, Less, or any number of other things. Then we likely never would have needed a special extension of the interface definition to define these constraints that accepted specific types. Additionally, people would be able to define equivalent "operator methods" on their own types, and everything would just work together seamlessly in the generics proposal, even if these user-defined operator methods were not actually treated as operators by the Go language... just as regular methods.
Why would Go not want to have all of this additional consistency? It would be a huge win for the language.
The only benefits of not providing methods on primitives that I can even think of all boil down to "it was easier to implement the compiler/language spec this way" or "this is the way it's always been done". Those aren't very compelling benefits.
I find it frustrating to try to contribute to these discussions sometimes. I apologize for being frustrated.

"this is the way it's always been done". Those aren't very compelling benefits.

This is the way it's always been done in this language is a very compelling benefit.

func GroupBy[T any, K comparable](slice []T, f func(v T) K) map[K][]T

func FlatMap[T, R any](slice []T, transform func(T) R) []R {}

These are potentially good ideas. I think the signatures should be
func GroupBy[T, V any, K comparable](s []T, func(t T) V) map[K][]V

func FlatMap[T, U any](s []T, f func(T) []U) []U

There's no way to preallocate for them, so it doesn't make sense to have Append variants.

More thoughts:

Go needs a deque type. Should it go in slices or container/list?
Similar question for set. Does it go in a maps package or somewhere else?
Planning out the maps package here doubles the number of bike sheds to paint, but there should be some thought now about how to create naming conventions that will work across packages.

Personally, I'd prefer to not have any package like the above. I'd much rather that we heavily utilize the functionality and keywords that Go already has going for it: range. Currently range is just a keyword for for loops, but there's nothing that prevents it from being more, and then building off that.
I'd like to see the range keyword return a typed Range that can be used in implementing all the functionality proposed by others here (similar, but simpler than D's ranges). If the range type had both an input and output type, then lots of possibilities suddenly spring to life, and the implementation of the Range interface can be left to the user for their own types, as well, and suddenly gain numerous benefits.
For example (I haven't kept up with all the go generic proposals, so apologies for the bad pseudo-code, it's been a while since I wrote production Go code and am rusty):
s := []int{1, 2, 3, 4, 5, 6}
r := range s

// functionality for simple range that outputs the same type as its input
odds := r.Filter(...)
squares := r.Map(...)

// transform the range output type to go from slice to map
r2 := r.Into[map[int]string]()
dict := r2.map(func (x int) (int, string) {
  return x, fmt.Sprintf("%d", x)
})

If there was a dead-simple interface for which "if my type implements this, the range keyword works and returns a InputRange" then you get half of that for free (Filter, Map, etc). If your type implements another interface, then you also get Range.Into for your type, and it's possible to provide lambda functions that allow users to transform from maps, slices, etc. into your type.
type Range[C, T] interface {
  GetRange() InputRange[T]
}

type InputRange[T] interface {
  Next() T
  AtEnd() bool

  // implementations of Filter, Map, ... here
}

type OutputRange[C,T] interface {
  New() C
  Put(into C, x T)
}

Obviously a lot of hand-waving and implementation details missing here, but doing something like this would allow for a whole lot more flexibility down the road.
I'd very much like to know that if I implemented a Set type, that all I need to do is implement GetRange and return a SetRange struct that implements two simple functions, and then I get Map, Filter, ... all for free, and can direct the output of those functions into a slice, map, another Set, or some completely arbitrary data type from another package (like a DataFrame).
My 2p.

More thoughts:

Go needs a deque type. Should it go in slices or container/list?
Similar question for set. Does it go in a maps package or somewhere else?
Planning out the maps package here doubles the number of bike sheds to paint, but there should be some thought now about how to create naming conventions that will work across packages.

Existing container/heap is a candidate for a generics based slice implementation too.

Is there some advantage that the proposed signature of IndexFunc with a v argument has over a simpler one like below? (Similarly for ContainsFunc etc.)
func IndexFunc[T any](s []T, cmp func(T) bool) int

Friendly reminder: it's easier to add new functions later rather than throw all possible generic slice func from a 1st iteration.

Why do Equal() and others state that the elements are compared in index order?  This blocks future optimizations such as parallel search, binary search, and so on.  Why lock yourself in?

What is the intention for the sort package with generic slices?  For example
slices.Sort(people, func(p1, p2 Person) bool {
    return p1.Age > p2.Age
})

is better than
sort.Slice(people, func(i, j int) bool {
    return people[i].Age > people[j].Age
})

2. My experience with languages like JS and Java (and quite a few others which were more C-like

Even C has functional programming with function as parameters.
int compare (const void * a, const void * b)
{
  return ( *(int*)a - *(int*)b );
}

// ...

qsort (values, 6, sizeof(int), compare);

There are some times where functional programming is needed to add developer freedom for extensive implementations.
Just like in C where functional programming is not even supported that much, but it was added in some places nonetheless because they get that there is a value to this.
Imagine making a quick sort in C that only sorts based on the implementation of the developers of C language. It would lose half of its value and it would make codebases much more complicated anyway.
I see no reason to not add the library functions with functional programming paradigms. I completely understand where you're coming from, functional programming can become uglier than regular flat code and make the code look more complicated. But so do Generics, doesn't mean they're harmful. Generics also make code look a bit more noisy, but like functional programming, they make codebases less bloaty. That is ... you dont have to rewrite same functions over and over.

Why do Equal() and others state that the elements are compared in index order? This blocks future optimizations such as parallel search, binary search, and so on. Why lock yourself in?

Perhaps as this is intended for slices, and a slice of [1,2,3] is different than [1,3,2] even if they all contain the same elements. You wouldn't be testing for equality, but for an intersection of values then, which could/would be an extension of the proposed API above.

That said, I wonder if something more analogous to strings.Replace could cover the more advanced use-cases of both Remove and Insert:

It does reduce readability a bit.  I have to be a lot more selective on what operation is intending vs just browsing the function name and the passed in arguments.

@TomOnTime, the order of comparison matters both in terms of efficiency (if you know they're in order you might prepare the inputs to shuffle likely-different elements toward the front), and in terms of semantics (if one of the comparisons panics, it really matters whether you perform that comparison before or after one that merely returns false).

	w = slices.Insert(w, i, v)
	x = slices.InsertSlice(x, xs, i)
	y = slices.Remove(y, i)
	z = slices.RemoveSlice(z, i, j)

could all be expressed as:
	w = slices.Replace(w, i, i, []T{v})
	x = slices.Replace(x, i, i, xs)
	y = slices.Replace(y, i, i+1, nil)
	z = slices.Replace(z, i, j, nil)

The former is self explanatory and self documented.
The later would generally end up requiring a comment next to the code.
Code maintainability should also be considered in this process, IMO.

What about HasPrefix() and HasSuffix()? :)

Some additional ones I write:
people = slices.Shuffle(people) // another func w/ input random
people = slices.Reverse(people)

Shuffle is already in package rand. (Whether/how old packages can be upgraded to use generics is a different question.)
Reverse is a good idea. I put it on my list.

That said, I wonder if something more analogous to strings.Replace could cover the more advanced use-cases of both Remove and Insert:
// Replace replaces the elements at s[i:j] with the elements of si.
// This modifies the contents of the slice s; it does not create a new slice.
func Replace[T any](s []T, i, j int, si []T)

I don't understand how that could act as a replacement for Insert when it has no way of growing s.

@Merovius

Personally, I'm a bit concerned about Index and especially Contains. IMO they suggest that unordered slices are a suitable data structure for sets.

I think that the functions are useful enough to warrant their inclusion, and that this could be alleviated relatively easily just by the existence of a container/set package, possibly with a note in the comments saying that that's better for use as a general, unordered, de-duplicated container.

@DeedleFake Python has a builtin set data structure and I still had to explain to several people several times, that their usage of x in y for a list made a linear algorithm into a quadratic (or sometimes cubic) one. So I disagree that the existence of a set data structure really solves this.
That being said, I'm not even saying these functions shouldn't exist (though TBH I find it hard to come up with a good use for Contains). Just that I'm concerned.

@rogpeppe, that's a good point. 😅 I've updated the code comment in my suggestion.
But! I would certainly buy that the subtleties of reallocation are reason enough to keep Insert and Remove separate.

I brought this up on a side conversation on slack and bringing back here for discussion:
Since there are a lot of different concepts, use cases, etc that would be introduced with this package does it make sense to delay putting it in the stdlib until after we get a feel for how these function would work in the real works.
This could follow the error wrapping proposal.   Create packages under golang.org/x/exp and then gain some real work experience on how they work and what works and what doesn't for inclusion into Golang after generics lands?
For example golang.org/x/exp/slices.  Keep pretty much the same proposed functions as listed at the original post.  See how they work and bring a subset (or even a superset) of them to the stdlib after some level of bake time.  Also should cut down on a fair amount of the bikeshedding (which is bound to happen again with maps package and a set package and a chans package if they were to be added as well.

@acacio

The former is self explanatory and self documented.
The later would generally end up requiring a comment next to the code.
Code maintainability should also be considered in this process, IMO.

Insert and remove are O(n) operations. Having a convenient function for them suggests that you ought to be using them. That's why I think having Concat/AppendConcat is better, but I think practicality may dictate letting them in anyway.
@Merovius

though TBH I find it hard to come up with a good use for Contains

For a small number of strings, it is sometime preferable to just do a few comparisons in a loop instead of a map construction followed by a lookup, but I agree that it depends.

I'm conflicted on the behavior of MapInPlace, because it sort of feels like it should act like copy(), which will just copy as many items as it can, but I think that implies that it should return the number of items copied. I have also sometimes wanted a thing which is like that, but can take an existing slice, and reuse it or grow it as necessary, but I think that's getting too complex.
For CompareFunc, what happens if the user's comparison function returns a value other than 0 or +/-1? Does it just return whatever that value is, or does it return one of +1/-1/0?
Also since there's no point commenting without at least one atrociously bad idea:
func Zip[T1, T2 any]([]T1, []T2) []struct{T1, T2}
func ZipMap[T1 comparable, T2 any)([]T1, []T2) map[T1]T2

I'm conflicted on the behavior of MapInPlace, because it sort of feels like it should act like copy(), which will just copy as many items as it can, but I think that implies that it should return the number of items copied.

Map always returns a slice of the same length as the initial slice. Are you thinking of FlatMap?

I have also sometimes wanted a thing which is like that, but can take an existing slice, and reuse it or grow it as necessary, but I think that's getting too complex.

That's how AppendX works. It's quite a nice idiom when you get used to it! It can be used in place or for creating a new slice or for adding onto an existing slice. See https://pkg.go.dev/strconv#pkg-functions for existing examples.

In the code that I write, these operations come up rarely.  Something like slices.Equal comes up frequently in tests, but the output of cmp.Diff is better for debugging failing tests (and all the transformation plugins are nice). I feel like the cases where I most need library functions like this is when dealing with sets, typically implemented as operations on map[T]struct{}. I have definitely written union/intersection functions over these types before, again and again, and because it's not generic I end up writing tests for these each time, which is annoying.
I fear that the easy availability of library functions for slices.Contains will encourage people to represent sets as slices, and write something like:
for _, a := range as {
   if slices.Contains(bs, a) {
      return true
   }
}
return false;

This will be speedy in their unit test where as and bs are a handful of elements, and then crash in production where the slices are longer and the O(n*m) complexity bites them.  But, it's not really up to the language to make people write good code -- the code reviewer should catch this.
Some comments mention that library functions shouldn't be O(n) complexity.  strings.Contains and sort.Slice are counterexamples.  Both are beneficial because strings.Contains delegates to strings.Index, which does a better job than what you would type into your code without thinking much.  sort.Slice is similar -- nobody really wants to lookup quicksort/mergesort just so they can present their list of users in alphabetical order.  (Digressing a little, I probably most use sort.* to make tests pass, which means I really want an unordered set instead of an ordered slice.  I'm officially a set crazy person, sorry about that.  I tell myself I'm being nice to API consumers by sorting the results of my function, but I know that the API consumer will never trust that they're getting a sorted list back, so every time I sort I'm burning O(n log n) operations twice ;)
Anyway, my overall thought is that the majority of these functions are easy to implement in an efficient manner with for loops, which has the benefit of not hiding the O(n) nature from code reviewers.
I have specific complaints about CompareFunc.  Imagine you have a slice of URLs, and you want to see if the server returns the same Content-Type header for each.  The signature forces you to share a global context.Context for the entire comparison (instead of for each comparison), and to panic on error (or otherwise hide the error from the caller):
as := []string{"http://example.com/test.txt", "http://example.com/image.png"}
bs := []string{"http://example.com/another-test.txt", "http://example.com/another-image.png"}
result :=  slices.EqualFunc(as, bs, func(a, b string) bool {
    resA, err := http.Head(a)
    if err != nil {
      panic(err) // !!!
   }
   resB, err := http.Head(b)
   if err != nil {
     panic(err)
   }
   return resA.Header.Get("content-type") == resB.Header.Get("content-type")
})

This sounds contrived, but doing I/O and encountering hangs/errors is fairly common.  I think this API encourages people to write worse code than if they typed in the slice equality function manually.  But, because the example is so contrived, it's hard to tell.
My TL;DR is that I want sets.Union and sets.Intersection where type Set[T any] map[T]struct{} more than I want any slice function.  But that is off topic for this discussion, so I apologize for the digression.

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

This sounds contrived, but doing I/O and encountering hangs/errors is fairly common. I think this API encourages people to write worse code than if they typed in the slice equality function manually. But, because the example is so contrived, it's hard to tell.

Planning for I/O in a comparison function seems like a complete anti-pattern.  I am happy with the existing function call to, at least, highlight abuse like this.   This is much better served with a for loop.
That being said it does bring up interesting questions around error handling in map/filter.

Planning for I/O in a comparison function seems like a complete anti-pattern.

Yup, fair enough.  (To some extent, it is hard to tell what is I/O and what isn't.  Your chunk-o-memory could be in the processor's L1 cache, or it could be an mmap'd file on a fileserver on Mars.  The API is the same but the expected error rate and latency are very different.  That is not up to the slices library, however, so I'm happy not discussing poorly-chosen EqualFunc functions.)

EqualFunc appears to me unfortunately named because the eq function could be any boolean operator.
f := slices.EqualFunc[int](a, b, func(x,y int) bool { return x < y })

would make sense and be useful code, but doesn't compute equality. BoolOp may be a better name.

@ulikunitz, maybe slices.Pairwise, as in “are a and b pairwise-equal?”

Why not use constraint type inference so these work for any type S []T?
Not sure if it meets the bar, but I've found Any/All functions to be useful. (Take a slice and a predicate func and return true if any (resp. all) items in the slice cause the predicate to return true). I have definitely written Any at least a dozen times in Go. Those could be done in terms of Reduce but that can't short circuit.

Personally, I'm a bit concerned about Index and especially Contains. IMO they suggest that unordered slices are a suitable data structure for sets. Anecdotally, using x in y for a Python list was the single most common mistake I had to deduct points for [edit] in an algorithms and datastructure class. Really need to hire a copy-editor [/edit]

Would using Find rather than Contains help to indicate to a user that the implementation is just a for loop over the elements?

@lollipopman I subjectively think Find would indeed be a better name than Contains.

Re: Find vs Contains
Usually Find implies it will return an object that was matching the predicate, where is Contains traditionally just a check (returning bool) to see container has an object matching a predicate. I would vote to keep it as is right now.

@lollipopman, @Merovius Find infers that is shuld return the postion of the find so it's the same as the proposed Index
Although Contains too can be easily written in terms of Index. I don't agree however to not include it just  because some people might mistake it to be a suitable datastructure for sets. Plenty of current code already returns slices of various types and often enough the size of these is small enough that the overhead of creating a proper set type to test for existence of an element alreday eliminates what benefit you get.

Another possibility would be to retain Index/Find and just drop Contains. slices.Index(vs, v) >= 0 is not that much harder than slices.Contains(vs, v).

I'd be good with leaving Index/Find in and dropping Contains. I don't think writing/reading Find(vs, v) >= 0 is that much worse than Contains(vs, v), but Index/Find is harder to mistake in terms of cost.
And to be clear: I don't think the most obvious alternative to look for an element is to put the data in a set datastructure - I think the obvious alternative is to sort the slice once and use binary search from then on. The cost of sorting is quickly amortized, especially if the number of elements is small enough to make a linear search tolerable. Granted, sorting only works for ordered types.

One thing that's perhaps worth pointing out here is the close relationship of this proposed package with the bytes and strings packages. I think it's worth preserving that relationship where possible.

One thing that's perhaps worth pointing out here is the close relationship of this proposed package with the bytes and strings packages. I think it's worth preserving that relationship where possible.

good point

Functional style at the level of slices isn't the right place, imo.  If that's your desired style, go all the way with iterators and/or views that can be composed, then materialized back into a slice if needed.  Basic slice utility like Equals, Compare, Index, Insert[Slice], Remove[Slice], Resize, are nice.
If they are included, IndexFunc and ContainsFunc should probably take a predicate instead of a value and a comparator.
Might (idx int, ok bool) be more idiomatic for Index's return value than returning -1 on "not found"?

In support of @cespare's comment, I wanted to demonstrate a poor UX quailty of some of the functional operators in this package. In java on might combine map/filter like so:
list
                        .stream()
                        .map(e -> fetchMetadata(e))
                        .filter(Objects::nonNull)
                        .map(m -> serialize(m))
                        .collect(Collectors.toList()));

In the proposed api, this becomes:
slices.Map(slices.Filter(slices.Map(list, func(l) {return fetchMetadata(l)}),
  func(o) { return o != nil }),
  func(m) { return serialize(m) });

What I don't like about this is that your eyes have to bounce back and forth between the list of operators (map, filter, map) and the list of actual functions, and figure out which function applies to which operator. I find this hard to read.

@hherman1: beat me to the draw; I too fully support the flow style, but it looks like the authors feel that implementing methods on interfaces (or generic parameters) is too confusing since there should only be "one way to do things": #39799. (This has some contradictions, since you can call sync.Map.Delete(sync.Map{}, "") or (&sync.Map{}).Delete(""), but they seem resolute.)
I think the response you will get is either reformat: (which imo is more readable, but still reverses the order of operations because prefix operators, and violates the line of sight rule)
slices.Map(
        slices.Filter(
                slices.Map(
                        list,
                        func(l) {return fetchMetadata(l)}
                ),
                func(o) { return o != nil },
        ),
        func(m) { return serialize(m) },
)

but more likely rewrite it with a bunch of temporary variables: (there are probably some perf implication, but I tend to prefer not making vars when not required)
one := slices.Map(list, func(l) {return fetchMetadata(l)})
two := slices.Filter(one, func(o) { return o != nil })
three := slices.Map(two, func(m) { return serialize(m) })

and this completely ignores the inergonomic nature of having to write the full func (x Xxx, y, Yyy) Zzz { ... } every time?

@hherman1, to be fair, you're creating a bunch of unnecessary anonymous functions.

slices.Map(slices.Filter(slices.Map(list, fetchMetadata),
   todo.NotNull[Metadata],
   serialize);

Your point stands, though, that java's syntax would have supported other processing than just function calls.
Your example also makes clear, if you are going to define these higher-order functions (which, again, I don't think should be done here), putting function first and container last tends to make them nest a little nicer:
slices.Map(serialize,
   slices.Filter(todo.NotNull[Metadata],
      slices.Map(fetchMetadata, list)))

I have some concerns about not having a plan for custom comparability, considering the footgun that is time.Time (see 45961 and 22978) and other pseudo comparable types. If we implement Equal and EqualFunc, technically experienced operators may know that you should call slices.EqualFunc([]time.Time{...}, []time.Time{...}, time.Time.Equal), but many people do not now you can even reference the time.Time.Equal symbol directly and the other will still compile. Is it worth investigating a holistic solution to custom comparability before exposing these symbols?

@hherman1, to be fair, you're creating a bunch of unnecessary anonymous functions.

you are not wrong, but there are cases where this is required:
slices.Map([]int{...}, func(i int) int { return int(math.Abs(float64(i))) })
slices.Map([]string{...}, func(s string) int { i, _ := strconv.Atoi(s); return i })

@hherman1 @colin-sitehost FTR, Map, Reduce and more - i.e. the functions that seem to benefit most from being methods, in your arguments - can't be implemented as methods, because that would require extra type parameters on methods, which the generics design does not allow, for good reasons.
So, the discussion "do we want methods or do we want functions" is moot for these functions. The question is just "do we want functions or do we want nothing".
Some of the functions (like Equal, Compare, Index, Contains…) could be methods, though.

I think this proposal is great for giving us the simplest solution to the problem of having to write tedious for loops for every basic slice operation. I don't think it needs to be any more complicated that this. This is most likely all that 90% of Go developers will need. Those 10% will develop their own solutions and some will share it with the community like they already do today.
Anyone here hoping to get a chainable Java Streams/.NET LINQeqsue API should also temper their expectations. Unless I'm missing something then the current generics proposal wouldn't allow you to chain calls so freely. The proposal states that methods can't define their own type parameters.

Methods may not take additional type arguments
Although methods of a generic type may use the type's parameters, methods may not themselves have additional type parameters. Where it would be useful to add type arguments to a method, people will have to write a suitably parameterized top-level function.

Look at this example
Map(T1 -> T2).Filter(T2).Map(T2 -> T3)

If you want to write this then T3 needs to be known at the point of Filter(T2) because Filter(T2) would need to return some generic type with a method capable of mapping T2 to T3. That type would have to look something like Stream[T2, T3]. We can get halfway there, but it's never going to be as fluent.

imo, I think we may finally have a compelling case for tuples (#32941), though I do not plan to hold my breath:
/* var s [](int, error) */ s := slices.Map([]string{...}, strconv.Atoi)
/* var s1 []int */ s1 := slices.Filter(s, func(i int, err error) (int, bool) { return i, err =! nil })

as I write this out, multi return looks more and more like a hack for error handling, because I really want an either/result, but I think the tuple usecase stands for cases where you really do want to return multiple meaningful values, like func xxx() (int, string, error).
I could see the feedback, that you should do all this in one function, though this kills composability and forces the use of a closure, killing testability:
var e error
s := slices.Map([]string{...}, func(s string) int {
        i, err := strconv.Atoi(s)
        if err != nil {
                e = err // maybe e = append(e, error) is better
        }
        return i
})
if e != nil {
        // uggh
}

I'm not sure I'm totally sold on the Java-style chaining.  It's nice in Haskell (where lazy evaluation "does what you mean"), and Python folks do like their list comprehensions, but I think the boring old Go way is pretty readable and pretty writable.
For my own code post-generics, I'm going to be very careful about when I use generics.  Like interfaces, the temptation is to overuse, and then have a program that is harder to read and debug.  When you need them, you need them, but going out looking for a place to use a programming language feature is something that scares me.  Writing things in terms of map/filter/reduce instead of for loops is something I will personally need a very good reason to do, and I haven't found that reason yet.  (I do it in Python because it's the idomatic way, of course.  Which way will be the One True Way in a Go that has the slices library?  Does Go even want a One True Way?)

@colin-sitehost I can see the appeal but I don't think it's very convincing argument here. It's not like using this package will be a necessity for all slice related operations in the future. We will still have our fine and dandy for loop, which seems like the best solution to your little problem there.
strs := []string{...}
var result []int
for _, s := range strs {
    i, err := strconv.Atoi(s)
    if err != nil {
        // handle error as usual
    }
    result = append(result, i)
}

I think it's just the case of choosing the right tool for the job. If a Map function that handled errors was something you really wanted, then it's trivial to implement yourself.
func MapErr[T1, T2 any](s []T1, f func(T1) (T2, error)) ([]T2, error)

https://go2goplay.golang.org/p/r37CKojOS2T

@jasonfran
I can see the appeal but I don't think it's very convincing argument here. It's not like using this package will be a necessity for all slice related operations in the future. We will still have our fine and dandy for loop, which seems like the best solution to your little problem there.

take care, all these examples are toys [recte strawmen], so creating counter examples is usually pretty easy, but when I have four functions all with different return types (and more complex logic than call "function and append") this gets really painful.

I think it's just the case of choosing the right tool for the job. If a Map function that handled errors was something you really wanted, then it's trivial to implement yourself.

assuming that I only return a thing and an error,  what about the case where length of the number of returns changes? now we are back to the state we are in today where I have to duplicate a bunch of code, simply because the return types are incompatible.

@jrockway

Does Go even want a One True Way?

apparently: https://github.com/golang/go/issues/39799#issuecomment-670213008

@colin-sitehost

take care, all these examples are toys [recte strawmen], so creating counter examples is usually pretty easy, but when I have four functions all with different return types (and more complex logic than call "function and append") this gets really painful.

assuming that I only return a thing and an error, what about the case where length of the number of returns changes? now we are back to the state we are in today where I have to duplicate a bunch of code, simply because the return types are incompatible.

I know they're toy examples, but I guess the point is that this is just the nature of trying to provide generic functions. Generic problems complement generic solutions. I assume the goal of the package isn't to provide a solution to every single person's problems. It's to find a solution for the majority of people. When you deviate away and need some more bespoke behaviour then that's something you need to develop yourself. We already do that today. For most people a basic Map function suits their needs perfectly fine.
My inclination if I was dealing with lots of return parameters would be to wrap it up in a struct.
And a future where 90% use the standard library and 10% define their own slice functions that the standard library doesn't cover sounds like a huge improvement to me.

@jasonfran

I assume the goal of the package isn't to provide a solution to every single person's problems. It's to find a solution for the majority of people. When you deviate away and need some more bespoke behaviour then that's something you need to develop yourself. We already do that today. For most people a basic Map function suits their needs perfectly fine.

First, maybe we should pull in the reference repo, since this sounds like conjecture or anecdotes (I will admit to that too), and see what "majority of people" means. I will also counter that this "just rewrite it yourself" is the thing that I see generics trying to solve. Many people have argued that go does not need generics ever.

My inclination if I was dealing with lots of return parameters would be to wrap it up in a struct.

This is apocryphal (at least it is not how the stdlib is written), and only works for symbols you control. How to you wrap strconv.Atoi or time.Parse in a struct? should everybody define func Foo(in FooInput) FooOutput?

@colin-sitehost
It is conjecture. But I don't think it's far fetched to assume that a basic Map function suits the needs of the majority of users. If someone wants a tuple then they can just substitute in a struct.

I will also counter that this "just rewrite it yourself" is the thing that I see generics trying to solve. Many people have argued that go does not need generics ever.

Generics is trying to solve the generic patterns in your code. Iterating a slice to filter out an element is the same core logic no matter what slice. Mapping a slice is also a pretty generic operation.
This proposed package is probably what you could call "generic generics" as in it's supposed to provide a broad solution to slice operations. It's not going to solve everyone's problems. Map is fine, if you have code that can return mapping errors then define your own Map to do it. But this is what Map is in its purest form.
Trying to write code that is generic that also works on a varying number of return parameters is just one step too far for what this package is about.

This is apocryphal (at least it is not how the stdlib is written), and only works for symbols you control. How to you wrap strconv.Atoi or time.Parse in a struct? should everybody define func Foo(in FooInput) FooOutput?

Yes? If your one and only desire is to use generic functions for cases where they're clearly not the best solution.
I wouldn't do that, and I wouldn't tell anyone too. I'd just use a for loop like I stated previously. But if the goal was to write the code how you wanted to then you have no choice. A ParseError[T any] would be a handy type for this exact case if you wanted generic code.

I'm surprised actually and pleased to see that what I thought was a flaw in this proposal is actually an advantage! The ugliness of chaining these things makes them best suited for simple cases, and retains for as the king of complex transformation. I feel more ok with the functional methods now, because they are unlikely to pass a code review when chained.

I really think it's a mistake to attempt to jam a "blessed" slices package into stdlib along with the release of generics.  This is one of those things that's easy to Do Wrong and then be stuck with because of the Go 1 Promise.
I would propose:

Release generics and see what the community comes up with re map, filter, and friends, and what traction these things gain and how they fit with the existing Go idioms.
Based on those results, write an x/slices package as the "official, experimental" package, and see what traction it gains.  See what the bloggers say about it.  See what users say.  See what large projects do.
If there's something the community is really happy with, pull it into the stdlib and bless it.

This is rough territory for Go, because this package is based on the most significant language change in 10 years.  I don't see the benefit in rushing this.  The context package hung out in x/context for quite a while before going into stdlib, and I think slices  or similar would benefit from the same approach.
Another reason this is important to get right: this isn't http or xml or json or crypto, this isn't a specific subject where a rough edge will impact only that one package, this is about general handling of slices, which is fundamental to the language itself.  Go has existing techniques and tricks for working with slices, and these are part of idiomatic Go.  A package like this needs to work and feel perfect and not redundant or clashing with these tools.

@bcmills @carlmjohnson

That said, I wonder if something more analogous to strings.Replace could cover the more advanced use-cases of both Remove and Insert:
// Replace replaces the elements at s[i:j] with the elements of si.
// This modifies the contents of the slice s when possible; it only creates
// a new slice if len(si) > j - i.
func Replace[T any](s []T, i, j int, si []T)

Then:
	w = slices.Insert(w, i, v)
	x = slices.InsertSlice(x, xs, i)
	y = slices.Remove(y, i)
	z = slices.RemoveSlice(z, i, j)

could all be expressed as:
	w = slices.Replace(w, i, i, []T{v})
	x = slices.Replace(x, i, i, xs)
	y = slices.Replace(y, i, i+1, nil)
	z = slices.Replace(z, i, j, nil)

Instead of slices.Replace, you could use slices.Concat:
	w = slices.Concat(w[:i], []T{v}, w[i+1:])
	x = slices.Concat(x[:i], xs, x[i:])
	y = slices.Concat(y[:i], y[i+1:])
	z = slices.Concat(z[:i], z[j:])

I find this a little easier to think about than Replace.

We already have the ability to express these operations using only append (though possibly needing a few lines if allocations matter). I think much more important is having names that express the intent. I don't think we gain much if SliceTricks just becomes ReplaceTricks or ConcatTricks. It's just another link to add to our chat bot to pull up each time a new user asks how to delete an element from a slice.

2. Based on those results, write an `x/slices` package as the "official, experimental" package, and see what traction it gains.  See what the bloggers say about it.  See what users say.  See what large projects do.

this is kind of what golang.org/x/exp is for, as long as the core team can agree, I think putting anything in exp along with the 1.18 release would be beneficial.

There's been a fair bit of discussion on this issue already, so apologies for piling on but I figure this is useful data to have while planning this API. Additionally, I'm sorry for this long message but I went over all the proposed APIs and have a fair number of suggestions and accompanying rationale.
To help navigate this, some of the suggestions I make below are:

*Func variants should support different types
Callbacks should accept indexes, not values
slices.Contains should operate on subslices
Reduce, Resize should be omitted
MapInPlace, FilterInPlace, and Remove should be renamed

Read on for details.
Different types for *Func variants
Maybe the *Func variants of these functions should allow different types where applicable.
For example, EqualFunc would have the signature,
func EqualFunc[T1, T2 any](s1 T1, s2 []T2, eq func(T1, T2) bool) bool

This would address cases where you have, say, an []int and a []string and you want to match them:
left := []int{1, 2, 3}
right := []string{"1", "2", "3"}
slices.EqualFunc(left, right, func(i int, s string) bool {
    return strconv.Itoa(i) == s
})

Indexed callbacks
The functions here are intended to replace for loops for some common cases, so they should attempt to have at least as much information as the for loop would.
Instead of accepting exact values in callbacks to *Func variants, we should maybe have callbacks that accept indexes.
For example, EqualFunc would become:
func EqualFunc[T any](s1, s2 T, eq func(int) bool) bool

Usage:
slices.EqualFunc(left, right, func(i int) bool {
    return left[i].Name == right[i].Name
})

This gives implementations as much information as their manual for loop based variants would have had and obviates the need for adding indexed variants in the future.
This also unlocks stranger use cases like checking whether a slice is a reverse of another slice:
slices.EqulFunc(left, right, func(i int) bool {
    return left[i] == right[len(right)-i-1]
})

Proposal
Separately from above, I have some comments about the proposed functions. I considered them in context of functionality offered in the bytes and strings packages, trying to align where possible.

No changes: Equal, Compare, Index, Map.

EqualFunc: If the suggestions above are accepted, this becomes:
func EqualFunc[T1, T2 any](s1 T1, s2 T2, eq func(int) bool) bool

Usage:
slices.EqulFunc(left, right, func(i int) bool {
    return left[i] == right[len(right)-i-1]
})

CompareFunc: If the suggestions above are accepted, this becomes:
func CompareFunc[T1, T2 any](s1 T1, s2 []T, cmp func(i int) int) int

Usage:
var (
  users []*User  = ...
  names []string = ...
)
slices.CompareFunc(users, names, func(i int) int {
    return strings.Compare(users[i].Name, names[i])
})

IndexFunc: If the suggestions above are accepted, this becomes
func IndexFunc[T any](s []T, eq func(i int) bool) int

Note that the value being matched against isn't an argument at all because it's constant and not an element of the slice.
Usage:
i := slices.IndexFunc(users, func(i int) bool {
  return users[i].Name == wantName
})

if i < 0 {
  return fmt.Errorf("unknown user: %q", wantName)
}

fmt.Println("Found user:", users[i])

Contains: This does not align with bytes.Contains or strings.Contains.
package bytes
func Contains(b, subslice []byte) bool

package strings
func Contains(s, substr string) bool

package slices
func Contains[T comparable](s []T, v T) bool

This should be changed to accept a subslice to match. Users who wish to match a single item may use slices.Index(...) >= 0 (or IndexFunc).
func Contains[T comparable](s, subslice []T) bool

ContainsFunc: To align with the proposed change to Contains, this will operate on subslices too.
func ContainsFunc[T any](s, subslice []T, cmp func(T, T) bool) bool

Further, if the changes suggested above are accepted, this transforms into:
func ContainsFunc[T1, T2 any](s []T1, subslice []T2, cmp func(i, j int) bool) bool

Usage:
all := []int{1, 2, 3, 4, 5}
sub := []string{"2", "3", "4"}
slices.ContainsFunc(all, sub, func(i, j int) bool {
    return strconv.Itoa(all[i]) == sub[j]
})

Filter: If we use indexed callbacks, this becomes:
func Filter[T any](s []T, keep func(int) bool) []T

Reduce: I fear that this will promote code golf with heavily functional code that will perhaps hurt understandability of affected code versus a for loop. For example, even in the simplest case, the for loop is more readable.
sum := slices.Reduce(integers, 0, func(acc, got int) int {
    return acc + got
})

// Versus

sum := 0
for _, i := range integers {
    sum += i
}

Presence of this API will make it easy for more contrived uses where the accumulator is a complex object, or an empty object to avoid writing a for loop.
names := slices.Reduce(users, []string(nil), func(names []string, u *User) []string {
    return append(names, u.Name)
})

// Versus

names := make([]string, len(users))
for i, u := range users {
    names[i] = u.Name
}

I'm of the opinion that this should be omitted from the initial version of this package.

MapInPlace: The name was non-obvious to me. This appears to be a copy-and-transform function. Given the existing trend of *Func functions, maybe this can be named CopyFunc. That name has the advanage of also making the len semantics obvious, in that they would match the copy built-in: copy min(len(dst), len(src)) items into dst.
func CopyFunc[D, S any](dst []D, src []S, f func(S) D)

If the suggestions to *Func variants above are accepted, this would become:
func CopyFunc[D, S any](dst []D, src []S, f func(int) D)

Usage:
names := make([]string, len(users))
slices.CopyFunc(names, users, func(i int) string {
    return users[i].Name
})

FilterInPlace: I'd like to suggest an alternative name here: RemoveAllFunc, and invert the filter function. Given the existence of other Remove* functions, this would make the in-place nature of this more obvious.
func RemoveAllFunc[T any](s []T, remove func(T) bool) []T

Additionally, if with indexed callbacks, this becomes:
func RemoveAllFunc[T any](s []T, remove func(int) bool) []T

Usage:
slices.RemoveAllFunc(users, func(i int) bool {
    return users[i].Status == StatusDisabled
})

Insert*: I mirror @bcmills' comment that a single Insert function should suffice.
func Insert[T any](s []T, i int, vs ...T)

Remove: Maybe this should be renamed to RemoveIndex to leave room for an equality-based Remove in the future?
func RemoveIndex[T any](s []T, i int) []T

// In the future:

func Remove[T comparable](s []T, v T) []T

RemoveSlice: I mirror @bcmills' comment that this could be RemoveN instead.
func RemoveN[T any][s []T, i, n int) []T

Resize: I mirror some of the other comments that the behavior and expectations for this aren't clear for this, and would be in favor of omitting this initially.

I welcome feedback on these suggestions and the rationale behind them. Thanks for reading my overly long comment.

If generics constraints (interfaces, contracts, or whatever we end up with in the end) were to allow expressing "something you can use for range with" and "something you can index", some functions in this package could be defined in terms of those, and then would be usable also with strings, maps, and channels.
That would be more akin to an "iterator" protocol with associated operations, as other languages either have from the begining or ended up retrofitting later.

The main difference between the indexed callbacks vs callbacks with values is that latter allows the callback to be a standalone function or method. With the indexed callbacks you need a function literal or if you want to reuse the function you need a HOF.
Consider the following examples:

Callback with values:

func EqualFunc[T1, T2 any](s1 []T1, s2 []T2, eq func(T1, T2) bool) bool

Usage:
slices.EqualFunc(names, users, func(name string, user User) bool {
  return user.Name == name;
})

or with a standalone function:
func matchUserByName(name string, user User) bool {
  return user.Name == name;
}

slices.EqualFunc(names, users, matchUserByName)

Indexed callback:

func EqualFunc[T1, T2 any](s1 T1, s2 T2, eq func(int) bool) bool

Usage:
slices.EqulFunc(names, users, func(i int) bool {
    return users[i].name == names[i]
})

or with a standalone function:
func matchUserByName(names []string, users []User)  func (i int) bool {
  return func (i int) bool {
    return user.Name == name;
  }
}

slices.EqulFunc(names, users, matchUserByName(names, users))

Though both are possible my personal preference is for the callbacks with values as it allows simpler and more conscious callbacks, that can be easily extracted as a separate function (or method) and reused.

I'd like to propose another function that I find often useful - BinarySearch. BinarySearch implements the binary search algorithm to locate an element in a sorted slice:
func BinarySearch[T ant](s []T, item T, cmp func(T,T) int) int

Parameters:

s - sorted slice
item - the value to locate
cmp - the comparer function

Returns:
The zero-based index of item in the sorted slice, if found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of len(s).
Usage:
i := slices.BinarySearch(names, "John Doe", strings.Compare)
if i < 0 {
 names = slices.Insert(names, ^i, "John Doe")
}

@svstanev Binary search probably belongs in sort. That's where it's already located.

One think to point out is that the Map and MapInPlace could be unified under MapAppend[D, S any](dst []D, src []S, f func(S) D), which appends to dst - Map would be equivalent to passing nil for dst and MapInPlace would be passing dst[:0]. Similar for Filter. Not saying we should, just that we could. And I find InPlace a slightly weird bikeshed color for Map, where D and S can be different, thus the mapping is never "in place".

I'd like to raise a concern that many developers that are new to Go have no idea how to remove an element from a slice. I see why having a single append function that could do everything would be meaningful in terms of keeping the spec short. However, there should be no such concerns when adding functions to the standard library. I think it's ok to have multiple functions that share a common base code, even if all they differ in is the initial argument list of said base code.

@hherman1
One thing you didn't mention about the Java Streams API is that the operators are lazy. Any computation is only done when a terminating operator is invoked. With that regard it is perhaps good that the functions mentioned here are just functions. This would perhaps discourage people from chaining multiple such functions together, and thus increasing their execution time. I personally see any usages from these functions as one-off calls that would replace an otherwise for loop with a more-or-less single statement.

@abhinav
Some disadvantages of indexed callbacks include being a bit unwieldy when the slice(s) being used is not just a simple word like left or right. When the user wants to use a slice that is a field of some struct, they would need to repeat the slice expression three times. This would hurt readability of the code a bit. It would also increase the chance of users mutating the slice during that operation.
I don't really think comparisons with existing APIs in the standard library should be used as a design factor. Different types might have different use cases for their methods. We shouldn't mimick them just because they were there already.

@Merovius yes, but in the context of generics sort will probably become more or less obsolete as it's main purpose, as I see it, is to provide a more generic approach to some common slice-related problems i.e sorting.
In the same vain slices.Sort is probably also a good candidate for the proposed package.

The main purpose of the sort package is definitely to provide sorting. It will definitely not become obsolete in any way.

@svstanev

func EqualFunc[T1, T2 any](s1 T1, s2 T2, eq func(int) bool) bool

I'm personally not a fan of the func(int) bool callback. It looks kinda funny to me that you do not give the responsibility of passing the elements to the EqualFunc when that it should be its job.
Point is that EqualFunc does not pass the elements by itself and that introduces some problems

The function loses one of its important responsibilities as an iteration function
Code becomes less clear and more smudgy. It gives the responsibility of getting the proper element from the array to the user. And I think it loses the whole point of these functions as iterators.
Callback function is also supposed to indirectly describe what the outer function does in a way.
You've got a CompareFunc, you need at least elementA and elementB to compare and test, and probably an index too as a bonus.
You've got a Map, you need an element and an index as parameters. They're describing in their own way what the function is doing and how it is supposed to behave. And abstracting that information from the callback function reduces the overall value of these iterator functions.

We can just pass both the element(s) and the index.

func EqualFunc[T1, T2 any](s1 T1, s2 T2, eq func(T1, T2, int) bool) bool

@svstanev To be clear: Slices are not the only thing that can be sorted or binary searched. It would be a net loss of expressive power, if we removed the sort package in favor of a theoretical slices.Sort (I know we can't remove packages anyway, but for the sake of argument).
So, we definitely will keep sort. We might provide an additional sorting function for slices. And that might live in slices. But it should then, to avoid code duplication, still just forward to sort. It seems unfortunate, to have the slices package import sort and pull in a lot of code many people might not need, if we can just as well have the sorting function for slices live in sort to begin with. Similarly for binary search.

@exapsy Exactly! Callbacks with values are my personal preference too! I just wanted to compare the pros and cons of callbacks with values vs the callbacks with indexes that @abhinav proposed and that is already used in other standard packages like sort.
As to passing the index along with the values in the callback, right now I cannot think of any particular scenario that it might benefit from having the index (probably unless you'd want to know that it's the 1st item i.e index == 0). Some methods of Javascripts' Array object like filter, forEach, map, reduce, etc. use a similar callbacks but they receive not only the index, but the Array itself. And having both the index and the Array available in the callback makes sense to me. Otherwise it's not much different than the callbacks with only indexes.

@Merovius I'm not arguing that the sort package should be gone. Only that it will become obsolete once we have a more generic approach to slices, sorting and searching.
In the context of the type system the sort package provides specialisation for sorting and searching in string, int and float64 -based slices so probably it should depend on the more generic slices package (once we have it) and not the other way around.
Other reason I see sort obsolete is that there are other common operations over collections (slices) that we don't have a dedicated package for, i.e indexing (linear search), filter, map, reduce, etc., and that the slices package can/will provide as I see it.
In conclusion I don't see these changes as a "bad" thing. Just the opposite. Generics are awesome and I believe that the entire ecosystem will benefit from them.

@svstanev

I'm not arguing that the sort package should be gone. Only that it will become obsolete once we have a more generic approach to slices, sorting and searching.

I understond what you where arguing. I strongly disagree with you.

In the context of the type system the sort package provides specialisation for sorting and searching in string, int and float64 -based slices so probably it should depend on the more generic slices package (once we have it) and not the other way around.

The slices package is not "more general". It is, by name, specific to slices. Those specializations for slices of string,int etc. will certainly become obsolete in favor of a generic SortSlice[T constraints.Comparable(s []T) function (or the like). But sort.Search, sort.Sort and other functions, as they are, can't be replaced by a function in slices, as they also operate on data structures which are not backed by a slice.
"Functions that operate on slices" and "functions that sort data and operate on sorted data" certainly have a non-zero intersection. But it is incorrect to say that one is a subset of the other.

@svstanev, @urandom, @exapsy
Thanks! Yes, those are valid concerns about indexed callbacks. I was certain that would be the most contentious piece of my proposal, which is why I tried to structure it so that we could leave out indexed callbacks and the rest would still be valid. I've updated my comment to list some of the points for and against them, and will defer to the Go authors and community.

For reference, this is what I'm currently using: https://github.com/thoas/go-funk

I’d like to remind ourselves of big.Int.Cmp, which looks like it could be used for slices.CompareFunc(xs, ys, big.*Int.Cmp). Is this a case of writing a new doc-example?

Naming the function Map() seems unfortunately inconsistent to me.  I think since it accepts a function as 2nd parameter it should named MapFunc() to be consistent with the rest of the functions in this package, even though that would mean there would (likely?) never be a Map() function.

I agree with you. I love consistency. The second parameter is a function f (T1) T2. I prefer 'MapFunc() ' to 'Map()'.

I wonder how this proposal evolves.
There are complainers, and as usual the complaints are loud and long. There are also like 800 people just liking this. What's the next step?

I wonder how this proposal evolves.
There are complainers, and as usual the complaints are loud and long. There are also like 800 people just liking this. What's the next step?
I don't remember seeing people arguing there should be no slices package, just over what goes in it and what the interface will look like, which seems to me like a good next step.

I think comments this dismissive and confrontational do not in fact help the proposal to proceed to the next step, and often have the opposite effect of causing people on both sides to dig in their heels and escalate to argument, making it difficult for people actually working on the next steps to communicate on the thread. So if you think objections are worthless and just empty complaining, it may still be in your interest not to say so.
 There is a pretty obvious selection bias here: the people who are more likely to comment or react are the people who are interested enough in a slices package to visit the page, read the proposal and comments, and weigh in.

We don't have generics yet. This proposal can't be implemented until we get them. There's no point in rushing. One way or another.

I wonder how this proposal evolves.

I would assume the way this proposal is going to evolve is the same the original generics proposal. It stays open for some months, to allow discussion and comments. Those comments will then be taken into account into occasional updates to the proposal. So that hopefully, when we have generics (or when they are at least in view), we know what the best version of this proposal is and can decide if we want to accept it or not.

There is a pretty obvious selection bias here: the people who are more likely to comment or react are the people who are interested enough in a slices package to visit the page, read the proposal and comments, and weigh in.

There definitely is a bias, but I don't think it's useful to argue in which direction it cuts. People who dislike generics or dislike the idea of providing a package such as this are just as likely to have seen and commented on it as people who feel the opposite.  There definitely is a bias towards people with an above average interest in contributing to Go and the discussion though.
Either way, the emoji votes are only a rough indication, the actual content of the discussion is what decides if and how this proposal is accepted.

There definitely is a bias, but I don't think it's useful to argue in which direction it cuts. People who dislike generics or dislike the idea of providing a package such as this are just as likely to have seen and commented on it as people who feel the opposite. There definitely is a bias towards people with an above average interest in contributing to Go and the discussion though.
Either way, the emoji votes are only a rough indication, the actual content of the discussion is what decides if and how this proposal is accepted.

Great points all. And I certainly don't want to get anyone sidetracked into figuring out which way it cuts. Thanks :)

@fzipp Thanks, fixed.

Thanks to everyone for all the comments.
@sfllaw
One of the unstated goals of this package is to minimize and perhaps eliminate https://golang.org/wiki/SliceTricks.
@sluongng
The intent is that Resize will return a truncated slice if len(s) > c.
The idea of lazy values is definitely for later.
I'm also inclined to separate out versions of the functions that return an error rather than panicking.  In most cases the caller will know that the function can't panic, so we will be left with a set of calls that ignore error results.
@bcmills
What should we call the "reduce cap to len" function?
It seems to me that Map/Filter/Reduce come up a lot in discussions of generic code.  And for real Go programs I think that covariance is a minor issue.  But I'm open to discussion.
The intent of Resize is to act like your suggested Grow function, except that Resize is flexible about the returned capacity.  This permits reserving space while keeping some of the advantages of append if it turns out that even more space is required.
Perhaps a Replace function is a good idea, but I don't think it would be a good to remove Insert and Remove in favor of Replace.  The explicit Insert and Remove express their intent clearly.
@HALtheWise @akavel
If we don't pass the value to IndexFunc and ContainsFunc, then the caller is forced to always use a closure.  Passing a value permits using general purpose functions, such as strings.EqualFold.
@carlmjohnson
I think that deque and set types should be their own packages.
@massung
A Range interface is an interesting idea, but it's a separate language change, and I don't see how it means that we would not want a slices package.
@notnil @svstanev
I tend to think that sorting should be kept to the sort package, not the slices package.  It's true that the sort package will require some thought to adapt to generics.
@jrockway
I agree that we also need a generic sets package.  But I don't think that the need for that means that we shouldn't have a slices package.

Probably a method for checking that every element of a slice satisfies a given criteria would also be useful:
func Every[T comparable](s []T, cmp func(T, T) bool) bool

@Merovius yes, and I think with Reduce to a boolean as well (without an early return possibility in this case though). It's only I have a self-observation that in languages that have both every and contains (however named) I never write if !contains. 🙂

Hi @ianlancetaylor

@sluongng
The intent is that Resize will return a truncated slice if len(s) > c.

It's still a bit confusing to end-user: would this trim the slice from the left or from the right?
What happen if new size is negative? Do we panic?

The idea of lazy values is definitely for later.

I disagree here in the same basis that some others have laid out in the comments:
Introducing Map/Filter would introduce another ways to do iteration loop over a slice which will
drive folks into a battle of 'which way is correct'.
Personally I feel like if lazy evaluation is not included, then there is not much reason to add these
helpers as I think it will worsen the collaborative experience.

I'm also inclined to separate out versions of the functions that return an error rather than panicking. In most cases the caller will know that the function can't panic, so we will be left with a set of calls that ignore error results.

That would be nice.
I wonder how the landscape of Golang will look like post generic?
Would something like Optional type in Java would be preferred over returning error?

I think the proposal should be split into multiple stages of implementation/rollout.
Some interfaces are clearly helpful to everyone(or most) who have commented here.
But some could wait out for 1-2 iterations / versions and can be re-evaluate then.

What should we call the "reduce cap to len" function?

Perhaps TrimCap? It's vaguely related to the bytes.Trim functions but operates on capacity rather than length.

It seems to me that Map/Filter/Reduce come up a lot in discussions of generic code. And for real Go programs I think that covariance is a minor issue. But I'm open to discussion.

They do come up a lot, but we (necessarily!) have no experience with writing Go code using them. I'm not opposed to considering them down the line, but I think bundling them in the first release with generics is extremely premature.
The fact that those functions come up a lot in discussion does not imply that they are actually a good way to address the underlying use-cases, especially given that Go's syntax and type system are very different from those of the languages where Map, Filter, and Reduce are commonly used. People may have have a hypothesis about what it would be like to program in Go using those functions, but we won't know how that hypothesis works out in practice until we actually try it out and write some real, nontrivial functions using them. That, to me, suggests an experimental library (perhaps in x/exp), not a core slices package in the standard library.

@ianlancetaylor 's comment about https://golang.org/wiki/SliceTricks is a good one.  It brings up a few cases where I've never thought to think about what I'm doing -- specifically, in-place filtering.  I pretty much always make a copy (if cond { xs = append(xs, x) }) and there is absolutely no reason in many cases except that I've never thought about it in any detail.
I am still not 100% sure whether it would be better to have a library function for both "filter by making a copy" and "filter by modifying the slice in-place", or to just have gopls suggest "hey you should read slicetricks and save yourself some allocations", though.  My worry is around having two ways of doing something; consistency is better than conciseness to me.  I think that the naive "filter by making a copy" is most clearly expressed as a for loop, but there is some trickery in "filter in-place" that is best represented as a library function.  But does it make sense to use a for loop for one, and a library function for the other?  Or should we just teach people the idiom for filtering in place, and eschew the library function in general?  I am totally undecided; just something for others to think about.
I agree with @bcmills about map/filter/reduce.  In Javascript codebases I've read, people tend to use these in favor of loops, and they don't compose well.  For example, I've seen code like:
const foo = everything.filter((x) => x.foo == 42);
const rest = everything.filter((x) => x.foo != 42);
rest.unshift(foo)
return rest

This makes two trips through the array for no good reason.  In Go, I'd write one loop that just moves elements where foo == 42 to the front.  Honestly I should go rewrite that Javascript to do the same, but I always get hung up on the 45 variants of for loops, and in my state of indecision defer that to some future date when I actually know what I'm doing in Javascript.  (for x in elements? for x of elements? for let x in elements? for const x in elements?  I don't really know.  I'd probably just install move-matching-elements-of-array-to-the-front from npm, honestly, and resign myself to a life of Dependabot notifications that a severe security vulnerability has been found in that library.  But I digress.)

I think that Uniq and UniqFunc are a mistake.
People who are familiar with the Unix uniq command (or, my googling suggests, C++'s std::unique) may well expect the proposed behavior (deduplicating runs of equal elements).
I think that a whole lot of other people will be surprised by the behavior, because what "Uniq" sounds like is that it's going to return only the unique elements. That is what Ruby's uniq method does, too:
irb(main):001:0> [1, 3, 1].uniq
=> [1, 3]

I'm not suggesting that Uniq should do this; nothing in this package should use O(n) memory. But this is what people will expect. I anticipate that this function will be misused much like strings.Trim is today: lots of programmers will expect that it does something different from what it actually does; it may appear to "work" as they expect, at first; and then we get lots of confused questions.
I also think that "finding all the unique elements" is more often what people need. I rarely need the proposed behavior; the only times I can remember using functions like that are as an optimization for finding the unique elements of already-sorted data without building a map. However, I frequently need to get the unique elements of an unsorted slice; I imagine that if we add a generic sets package the way to do that will be something along the lines of
sets.FromSlice(s).Slice()

If we keep Uniq and UniqFunc, which I don't think we should for a v1 of a slices package, I would propose renaming them to be more specific; perhaps UniqRuns or UniqConsecutive or something along those lines.

personally, I am fine with Uniq{,Func}; if they are performant, well documented, and useful, that should be fine. plus, there are already too many sharp edges in the stdlib to expect everything to "just work". there is an expectation that you read the docs: e.g. time.Time.Equal.
I also think that slices.Uniq(sort.Slice(s)) is not a bad ideom, esp since it reads like ... | sort | uniq in bash.

FWIW, I thought a lot about whether Uniq is a good idea or not when I was writing my stringutil package. I ended up deciding on having both Unique(dst, src []string) []string and UniqueVia(dst, src []string, m map[string]bool) []string. I am still not sure if that was the correct judgment or not for my needs, but I think it would be a mistake for the standard library to add a function with clear potential for beginner misuse.

I agree with @cespare that there is potential for confusion and would lean against it's inclusion in this form. I did need Uniq a couple of times though, so I think it would be useful to have in some form. Perhaps the potential for conclusion can be eliminated or reduced by naming or by the context it's placed in.
Is there ever a case where you want the constraint of Uniq to be comparable instead of constraint.Ordered? ISTM that to be useful, you need a guarantee that equal elements are consecutive and the way to get that guarantee is by sorting.
The reason I'm asking is because the one context where I had to write the code of Uniq is in the context of representing a set as an ordered slice. I hope we'll get a type/package for that at some point - and perhaps the place of Uniq is there, not the slices package, because it doesn't really operate on any slice, but only meaningfully on ordered slices?

What I don't understand: Why is an Equal and Compare for slices suddenly decidable? The current Go comparison operator doesn't support slices. What changed here and why not make the operator work instead for slices with comparable elements (and exclude slices of interfaces)?
@ianlancetaylor could you give some details in the proposal what changed here and why this trade-off (require a package vs. making == work for slices) has been chosen?

I definitely intuitively expected Uniq({1,2,1}) to return {1,2}, as
that is most commonly the behavior I want. Notably, the solution using the
hypothetical sets package isn't order-preserving or even deterministic, and
any solution involving sorting is O(n log(n)), which is undesirable.
On Tue, Jun 8, 2021, 11:25 PM Axel Wagner @.***> wrote:

I agree with @cespare https://github.com/cespare that there is
potential for confusion and would lean against it's inclusion in this form.
I did need Uniq a couple of times though, so I think it would be useful
to have in some form. Perhaps the potential for conclusion can be
eliminated or reduced by naming or by the context it's placed in.
Is there ever a case where you want the constraint of Uniq to be
comparable instead of constraint.Ordered? ISTM that to be useful, you
need a guarantee that equal elements are consecutive and the way to get
that guarantee is by sorting.
The reason I'm asking is because the one context where I had to write the
code of Uniq is in the context of representing a set as an ordered slice.
I hope we'll get a type/package for that at some point - and perhaps the
place of Uniq is there, not the slices package, because it doesn't really
operate on any slice, but only meaningfully on ordered slices?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/golang/go/issues/45955#issuecomment-857418110, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AAM6N6HFKX3WAEZGREUJECLTR4CNFANCNFSM44DVU24Q
.

@HALtheWise It's possible to implement such a generalized Uniq in order-preserving way, in O(n) time and memory:
func Uniq[T comparable](s []T) []T {
	out := s[:0]
	found := make(map[T]bool)
	for _, v := range s {
		if found[v] {
			continue
		}
		found[v] = true
		out = append(out, v)
	}
	return out
}

Yes, I am well aware of that solution and have coded it several times (this why I expected it in the standard library). I was simply pointing out that the previously mentioned one-liner workarounds brought up in this thread (sets.FromSlice(input).Slice() or slices.Uniq(sort.Slice(input))) don't satisfy my typical use case.

I guess there is the possibility to have all three:

Add a separate hash set package, which can be used if you just want to treat them as an orderless bag-of-things
Add the proposed version of Uniq to the sort package (or the like), together with other algorithms to operate on sorted data (like binary search)
Add a generalized Deduplication-function here, similar to the one above.

Personally, I'd be a bit sad if we added the last one, but not the second one - so far, I've ~never needed to remove duplicates from an unordered slice, but I use ordered, deduplicated slices quite frequently as sets.

Personally, I'd be a bit sad if we added the last one, but not the second one - so far, I've ~never needed to remove duplicates from an unordered slice, but I use ordered, deduplicated slices quite frequently as sets.

Do you find that you ever need this for types besides string? I find myself needing to deduplicate strings quite often, but other types essentially never.

Do you find that you ever need this for types besides string? I find myself needing to deduplicate strings quite often, but other types essentially never.

I think it depends on what kind of code you write. None of the concrete cases I'm thinking of used strings.

@Merovius

@HALtheWise It's possible to implement such a generalized Uniq in order-preserving way, in O(n) time and memory:

For the record, I think you'll find the time complexity of that is O(n * log(n)) because map access is O(log(n)).

@rogpeppe

For the record, I think you'll find the time complexity of that is O(n * log(n)) because map access is O(log(n)).

Hm? No it's not. map is a hash-map, it has O(1) access. What am I missing?

Let's not get hung up on the asymptotic complexity of map-based Uniq.
It remains a fact that non-map-based Uniq is going to be far faster regardless.

🚲 : How about the name Dedupe? To me, it implies removing successive duplicates better than Uniq does. To me, uniqueness is a global property, so my expectation is that it will create a set, like Ruby's uniq does. There's still a chance of misunderstanding Dedupe but less so.

@carlmjohnson to me Dedupe sound like deduplicate which literally means to remove duplicates. I don't see how it's better or worse than Uniq except that its worse only because it's longer and composed of two syllables.

@carlmjohnson to me Dedupe sound like deduplicate which literally means to remove duplicates.

It removes successive duplicates, as opposed to all duplicates. This is different from "unique" in that there's no such phrase "successive uniques" in my idiolect of American English. "Uniqueness" is a global property. It's reason the internet grammar people put forth for disliking the phrase "very unique". As I wrote "There's still a chance of misunderstanding Dedupe but less so."

@carlmjohnson Dedupe doesn't mention submissiveness though. That is, while you are right that "successive unique" as a combo makes little sense, that doesn't mean Dedupe implies (or even suggests) successive. I agree with @thomasf that Dedupe is no improvement.
FWIW, it can be argued that Uniq is a better name, because it's not Unique. That is, it intentionally deviates from the correct spelling in favor of the same spelling as the uniq command. While Unique is ambiguous, arguably, Uniq isn't.
But really, I agree that the name is too ambiguous and will be misunderstood and I don't think there really is a better one, aside from a monstrosity like RemoveSuccessiveDuplicates or something.

I wonder if the debate over dedupe vs. uniq and what the behavior should be is demonstrating that there is no such general, all-purpose function that deserves a general name that gives it pride of place?
TBH, I don't see the problem with something like RemoveSuccessiveDuplicates or UniqSuccessive. I'd much prefer that. Just because Unix tradition exists and the docs will exist doesn't mean we should force people to always go to the docs to remember what Uniq means in Go. Especially because other languages like Ruby have muddied the waters by using uniq to mean something else.
I do a lot of PHP, and plenty of docs exist. Even experienced developers spend a lot of time with them. It's not great.

I think the risk of people forgetting that Uniq does only works on consecutive items in Go is pretty high. When I am implementing something like json apis its not likely that I would be testing for slices explicitly for deduplication correctness if there is some Uniq going in somewhere in the code.
I have certainly forgot that the coreutils uniq behaves like this a few times and created shell scripts with unintended consequences.
Maybe it's ok if Uniq is slower and removes all duplicates or that we just have to bite the bullet and name it as something with an extra syllable like AdjacentUniq or whatever is needed to communicate the specifics of the operation a little bit closer.

I don't necessarily see why we can't have both functions. I often do the full deduplication variant on pretty small slices where the performance negligence of having a single method call in the stdlib would be ok. If you need more performance you can use the other one and if you need even more you probably going to use for loops anyway to do as much as possible in one loop.

I agree. I'd prefer some naming scheme that fully describes both and puts them next to each other, like UniqAdjacent and...well, the other one is hard. UniqFull ...is probably not it. But entirely avoiding having a function with just the name Uniq would be great.

If all is not considered to be the default there are a lot of functions you could suffix with All for no good reason. As long as the default short named functions behavior is the least likely one to cause bugs in a program from how it's named I am fine with it not being spelled out.

Yeah...it's just there's rarely ambiguity from the name having a lot of baggage. I guess Unique and UniqueAdjacent wouldn't bring baggage.

Given how much bikeshedding there is over Uniq/Unique/UniqAdjacent/etc, it does seem like we should avoid that word.
I'm inclined to just make up a good short name like we did for strings.Cut, and then it won't have any baggage. For example, Collapse or Compact. Rather than bikeshed that too, I'm happy to wait to see what Ian's next revision looks like.

I'd find some sort of generic Join method very useful, like bytes.Join:
// Join concatenates the elements of s to create a new slice. The separator
// sep is placed between elements in the resulting slice.
func Join[T any](s [][]T, sep []T) []T

Though I don't know how useful a separator would be with non-byte slices, so perhaps just:
// Join concatenates the elements of s to create a new slice.
func Join[T any](s [][]T) []T

I can also have situations where using varidac arguments would be preferable, so again perhaps:
// Join concatenates the elements of s to create a new slice.
func Join[T any](s ...[]T) []T

Isnt join just append if you leave the separator out?

Isnt join just append if you leave the separator out?

It is if len(s) == 2, but you can't append multiple slices like dst = append(dst, x..., y..., z...) which you could do with Join. It also only allocates once rather than multiple times if you append without properly sizing the slice.

// Join concatenates the elements of s to create a new slice.
func Join[T any](s ...[]T) []T

I believe the usual name for this operation would be Flatten.

Join naturally pairs with Split, but I don't see an obvious way to write Split.

The variadic version without the separator looks like a Concat

My preference is for Concat[T any](d []T, slices …[]T) []T. It’s versatile enough for use as a makeshift remove, insert, append all, etc.

Product?
// Product returns Cartesian product of input slices.
func Product[T any](a [][]T) [][]T

Product([][]int{ {1,2}, {3,4,5} }) => [][]int{ {1,3}, {1,4}, {1,5}, {2,3}, {2,4}, {2,5} }

Join naturally pairs with Split, but I don't see an obvious way to write Split.

func Split[T any](s []T, f func(int, Т)) [][]T

This would be pretty handy in situations where you have some sort of ordered slice which could lend itself to be split into groups.

My preference is for Concat[T any](d []T, slices …[]T) []T. It’s versatile enough for use as a makeshift remove, insert, append all, etc.

I had the same thought. The only difference between Join and Concat is that Join restricts its types more and includes a special separator. I appreciate that there are use cases for a generic join, but how common are they compared to bytes.Join?

func Split[T any](s []T, f func(int, Т) bool) [][]T
This would be pretty handy in situations where you have some sort of ordered slice which could lend itself to be split into groups.

This is a case where personally I'd rather write a loop.

With regard to Join aka Concat, see also the discussion at #4906, especially the comment https://github.com/golang/go/issues/4096#issuecomment-600317557.

I have only read the original post, so some of these questions may be addressed in the comments. Apologies for that but this is a very long thread.
The Equal and EqualFunc comments should clarify that no comparisons are made if the lengths are equal.
I don't understand what 'lexicographic' means in Compare. There is no lexicography involved. The following sentence repeats what I think it means, so the first is unnecessary.
What happens in Map if the slices are indeed the same. When does the iteration stop? Ditto for Filter.
I think the example in Reduce has the arguments in the wrong order. Or maybe in the right order, and the signature has them in the wrong order.
In the comment for Insert and others, !(i >= 0 && i <= len(s)) is hard to read and needlessly codey. "This panics if i is not in range."  Also, as in several other comments, "This" has an unclear referent. In fact, in several comments there are multiple "This"es with different referents. Rewrite for clarity.
Remove should be called Delete, consistent with the map element deletion operator.
Since you asked about RemoveSlice, I would prefer just one function with two integers.
Uniq and UniqFunc do not, in my opinion, carry enough weight to appear in the core library.
"Resize returns s with length c" is not what it does. At least it cannot always be what it does. Explain the allocation strategy.

@ianlancetaylor

If we don't pass the value to IndexFunc and ContainsFunc, then the caller is forced to always use a closure. Passing a value permits using general purpose functions, such as strings.EqualFold.

Hm; doesn't sound an optimization worth doing to me; please note some counterarguments:

with the proposed v in IndexFunc and ContainsFunc, passing 1-argument functions/"predicates" now becomes surprisingly cumbersome: it requires a closure + a spurious dummy/zero value - e.g. for unicode.IsSpace, or math.IsNaN:
 IndexFunc(nums, 0, func(a, b float64) bool { return math.IsNaN(b) }) // or is it `a`?
 // vs: IndexFunc(nums, math.IsNaN)

that becomes even weirder if I'd want to pass a closure with complex logic, that would not lend itself well to a simple "comparison" notion; then I both have to artificially introduce a fake ignored 2nd argument, and pass a dummy/zero one as v - e.g.:
 IndexFunc(nums, 0, func(a, b float64) bool { return 10 < b < 20 }) // or is it 'a'???`
 // vs: IndexFunc(nums, func(v float64) bool { return 10 < v < 20 })

as seen above, with complex functions it additionally becomes tricky to be sure whether a or b is meant to be v

seems like a premature optimization to me as well: a Sufficiently Smart Compiler™ might one day optimize out simple closures like: func(s string) bool { return strings.EqualFold(s, needle) }

@ianlancetaylor
I think it can be argued that "find the index of $element" and "find the index of the first element fulfilling $predicate" are two conceptually different, equally important use-cases and should have two different functions. If we are to only have one of them, I tend to agree with @akavel that the predicate-form is the more natural. It's more natural to express "find the index of element v" by writing the predicate "is equal to v using some equality function", than it is to express "find the first element fulfilling p" by writing the equality function "it's equal if one of the arguments fulfills p, ignoring the other". It would also be possible to write a function to transform a comparison function into a predicate, so we could write slices.IndexFunc(s, slices.EqualByFunc(v, strings.EqualFold)) or somesuch.
FWIW, precedent of the IndexFunc name also would favor the predicate-form. The equivalent to Index is arguably IndexByte.
If we want to have both (allowing both use-cases to be satisfied easily), naming consistently will be hard, but I'm sure we'll find something.

I have only read the original post, so some of these questions may be addressed in the comments. Apologies for that but this is a very long thread. (If so, perhaps it's worth updating the proposal to reflect any changes, and saying that that has happened.)

I've been updating the proposal based on comments.  Added a note to that effect.

The Equal and EqualFunc comments should clarify that no comparisons are made if the lengths are equal.

Done (assuming you mean no comparisons if the lengths are not equal).

I don't understand what 'lexicographic' means in Compare. There is no lexicography involved. The following sentence repeats what I think it means, so the first is unnecessary.

Done.

What happens in Map if the slices are indeed the same? When does the iteration stop? Ditto for Filter.

I wrote that badly.  Tried to clarify.

I think the example in Reduce has the arguments in the wrong order. Or maybe in the right order, and the signature has them in the wrong order.

Fixed.

In the comment for Insert and others, !(i >= 0 && i <= len(s)) is hard to read and needlessly codey. "This panics if i is not in range." Also, as in several other comments, "This" has an unclear referent. In fact, in several comments there are multiple "This"es with different referents. Rewrite for clarity.

Done.

Remove should be called Delete, consistent with the map element deletion operator.

Done.

Since you asked about RemoveSlice, I would prefer just one function with two integers.

Ack.  Leaving open.

Uniq and UniqFunc do not, in my opinion, carry enough weight to appear in the core library.

Ack.  Leaving open.

"Resize returns s with length c" is not what it does. At least it cannot always be what it does. Explain the allocation strategy.

Tried to clarify.
Thanks.

@akavel To be clear I wasn't suggesting avoiding closures because of optimizations.  I was suggesting it for code clarity.  But your arguments are convincing.  I'll change it.

That is, I changed IndexFunc, nothing else.

@ianlancetaylor I think the v T argument to IndexFunc is vestigial. At least I don't understand what it would mean.

@Merovius Thanks, fixed.

It would be cheap and easy to remove the unspecified behavior for Map by panicking if the slices overlap (except for the special case mentioned). I suggest doing that. Unspecified behavior leads to bugs and requests for changes to tools like vet to avoid misbehavior. Just kill it off now.

The intent of Resize is to act like your suggested Grow function, except that Resize is flexible about the returned capacity. This permits reserving space while keeping some of the advantages of append if it turns out that even more space is required.

The suggested Grow function is also flexible about the returned capacity — the integer argument is a minimum, not an exact quantity.
As far as I can tell, the difference between Resize and Grow is that Resize is defined in terms of length (with the change in capacity as a side-effect), whereas Grow is defined in terms of capacity (completely orthogonal to length).
If the intent of the function is to change the capacity of the slice, I think the function's behavior should be defined in terms of capacity, not length.

There’s another function in this family of functions that I think is even more likely to be directly useful, and that is the Reserve function. Reserve would be called just before appending a set of elements to a slice, and it would be called with the expected number of elements to append. It would guarantee that there is sufficient capacity to append that many elements without further allocation. Under the hood, this would likely implement the same amortization that growslice uses, so it would be fine to call this function repeatedly with a small value followed by a few appends without worrying about suboptimal performance as the slice scales up. If there is sufficient capacity in the slice already, Reserve is a no-op.
As an example of where this is useful, consider an API that returns batches of elements. One such API is the S3 list objects API. If code is collecting a list of all objects, things are perfect if we append each batch directly to the overall collection slice. Append will ensure there is room for the entire batch to be copied in one go.
However, if we need to do a type conversion, then we have to append one at a time. The current amortization from append is pretty good, but it is not optimal at all if the batches are coming in a thousand at a time. For the first batch, append will reallocate the slice what, 9 times? For the next several batches, append will be growing the slice 25% at a time, but that will still mean multiple allocations and copies per batch.
Further, let’s consider the case where we are doing a small, fallible transformation on each element. In general, we expect most elements to be appended successfully, but some will be deemed irrelevant and dropped.
If we called Grow(collection, len(batch)) each time, then by the end the slice would be unnecessarily large — there would be unused capacity for every element we dropped, and then some for amortization. So, then we have to call it with Grow(collection, len(batch) - (cap(collection) - len(collection))) to avoid gathering this ever-growing capacity ballast that isn’t part of the intentional amortization schedule.
Using Resize seems even clumsier here, since we would constantly be calling it like Resize(collection, len(batch) + len(collection))[:len(collection)], which is not something I look forward to reviewing.
With the Reserve function, it works the way you would expect. Each time, you just tell it how much you might append, and it operates at exactly that level, without offloading the mental overhead of thinking about how much extra capacity the slice has, or how long it currently is, or having to bother with re-slicing the result before you can begin appending to it. Simply Reserve(collection, len(batch)). Any unused reservation is fine — it just gets absorbed into the amortization schedule. If you repeatedly fail to use all of your reservation, the Reserve function will stick to a sensible amortization strategy and not continue growing the slice more than needed forever.
Hopefully I’m not misunderstanding any of the nuances of previous comments here.
For some prior art, Rust has a reserve function on its Vec type, and it is nice for use cases similar to this.

It might also be nice to have a ShrinkToFit(slice []T) function that can be used when you want to finalize a slice for longer term storage in memory without wasting memory on reserved capacity that will not be used for a long time, if ever. It would copy the slice into a new slice of exactly len(slice) capacity if the existing slice has any extra capacity.

@coder543, you are basically asking for syntax sugar around:
// var t []T
tmp := make([]T, len(t))
copy(tmp, t)
t = tmp

not sure if it reads that much better?

@colin-sitehost to be clear, that is just my second comment, which I separated because it is much more “nice to have” than Reserve.
you could even shorten your implementation of ShrinkToFit to append(make([]T, 0, len(slice)), slice…), I believe. If I saw a line of code like that in a PR review, I would absolutely want there to be a comment with it. It’s an obtuse piece of code that looks like it serves no purpose.
A well named function like ShrinkToFit is valuable because it makes the code more obvious, more readable, and less error prone. Initially I wrote that you could shorten your implementation to append([]T{}, slice…), but then I remembered that would probably have the exact same growslice amortization schedule kick in, and all it would accomplish is to waste execution time creating an equally oversized slice.

I think the pair of Reserve(s []T, n int) []T and Unreserve(s []T) []T (which would be simply return s[:len(s):len(s)]) would solve a lot of uses for me. I generally get in a slice and want to ensure that it has sufficient capacity for the known append items and then occasionally I want to be able to append to slice without worrying about anyone else holding a copy of the same underlying source. Reserve could ensures the returned slice has cap > len(s) + n without defining exactly what the cap amortization schedule is. One of the tricky parts of using slices now is that you can’t get Go’s default amortization schedule without using append directly.

One thing I haven't seen addressed from my earlier comment is whether EqualFunc, etc could accept two types without loss of expressiveness.  I think, with the type inference as proposed, it would be more flexible without making it harder to do the simple thing:
func EqualFunc[T1, T2 any](t1 []T1, t2 []T2, eq func(T1, T2) bool) bool {
	if len(t1) != len(t2) {
		return false
	}
	for i := range t1 {
		if !eq(t1[i], t2[i]) {
			return false
		}
	}
	return true
}

func main() {
	// Different Types
	t1 := []int64{1, 2, 3, 4}
	t2 := []int{1, 2, 3, 4}
	fmt.Println(EqualFunc(t1, t2, func(a int64, b int) bool {
		return a == int64(b)
	}))

	// Same Type
	t3 := []string{"pod1", "pod2"}
	t4 := []string{"POD1", "POD2"}
	fmt.Println(EqualFunc(t3, t4, strings.EqualFold))
}

// Output:
//   true
//   true

(https://go2goplay.golang.org/p/eT2VJe2HfpL)
I think this applies in one way or another for most of the Func variants, because the type inference can come from the typed slices and then the functions can be checked.  The error messages are already good in the go2go demo:
prog.go2:24:32: type func(a int, b int) bool of (func(a, b int) bool literal) does not match inferred type func(int64, int) bool for func(T1, T2) bool

I would extend this to two more XxxFunc symbols that accept func(T, T): can we get a strong argument for why [T1, T2 any] is bad for:
func CompareFunc[T1, T2 any](s1 []T1, s2 []T2, cmp func(T1, T2) int) int
func ContainsFunc[T1, T2 any](s []T1, v T2, cmp func(T1, T2) bool) bool

For the function to filter one can define an anonymous function in the call, but for repeated similar functions like "match regexp" it is better to provide some kind of library. Is the intention to generate "keep" functions from library code? I made a simple example;
https://play.golang.org/p/dpKz9-GpigF
No regexp but it shows the problem.
I have recently started to work with a similar "slices" package but only for []string and used an object instead of a function;
type Validator interface {
        IsValid(str string) bool
}
func Filter(v Validator, slice []string) []string

An advantage is that "Validators" can be unit-tested and provided in a library, and they have a context, e.g;
type RegExp struct {
        RegExp *regexp.Regexp
}
func (v RegExp) IsValid(str string) bool {
        return v.RegExp.MatchString(str)
}

I wonder if that is something you might consider? Also I want to keep my implementation as close as possible to a future go standard package, so if a "keep" function will be the way I will re-write my implementation.

Is the intention to generate "keep" functions from library code?

The only intention for Filter is to provide a flexible capability that people can use as they see fit.  Including your suggestion.
I think that requiring a separate type with an IsValid method will be harder to use for simple cases, and the current suggested implementation already supports it by writing code like
    r := RegExp{...}
    slices.Filter(d, s, r.IsValid)

A method can be turned into a closure by doing Filter(instance.Method, slice), but it doesn't work the other way around to turn an function into an interface (although there is a proposal to change that). You have to create a type and write three lines of boilerplate. For that reason, in my opinion, you should only prefer an interface to a closure when there are multiple methods either explicitly or implicitly. (Implicitly would be like how an io.Reader is only a one method interface, but it it related to io.WriterTo, etc., etc., so you may want to test for and invoke extended interfaces, so it's worth it to not be a closure.) In this case, there are no extended interfaces for filtering, so it should be a function.

@carlmjohnson You are absolutely right, I can do;
	c := &FilterContext{"foo"}
	fmt.Println(filter(slice, func (s string) bool { return c.keep(s) }))

and get the context I want. Thanks.

You can also do
	c := &FilterContext{"foo"}
	fmt.Println(filter(slice, c.keep))

I think
type FilterPredicate[T any] interface{ Keep(T) bool }

func Filter[T any, F FilterPredicate[T]](a []T, f F) []T

is much easier to optimize than
func Filter[T any](a []T, keep func(T) bool) []T

because Filter[T any, F FilterPredicate[T]](a []T, f F) []T instantiates a new Filter for every predicate and keep call is static.

I came across this issue when searching for the best way to clone a []string
https://github.com/go101/go101/wiki/There-is-not-a-perfect-way-to-clone-slices-in-Go
But there is no slices.Clone(). Is  that because "There-is-not-a-perfect-way-to-clone-slices-in-Go" :smiley:

Shall slices.Contains(nil, nil) return true or false?

Shall slices.Contains(nil, nil) return true or false?

false. A nil slice has length 0 and acts like an empty slice for other operations like append and range.