Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: slices: CopyFunc and AppendFunc #71277

Open
jimmyfrasche opened this issue Jan 15, 2025 · 7 comments
Open

proposal: slices: CopyFunc and AppendFunc #71277

jimmyfrasche opened this issue Jan 15, 2025 · 7 comments
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Milestone

Comments

@jimmyfrasche
Copy link
Member

Proposal Details

The copy and append builtins are well known and useful. Combining copy and append with elementwise mapping yields two useful functions that are easy to explain in existing terms:

CopyFunc is defined similar to copy but takes an additional mapping function to apply before assigning the src element to dst.

func CopyFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](dst S1, src S2, f func(E2) E1) int

The implementation for AppendFunc is simple and straightforward enough that I include it in full:

func AppendFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](dst S1, src S2, f func(E2) E1) S1 {
	dst = Grow(dst, len(src))
	for _, v := range src {
		dst = append(dst, f(v))
	}
	return dst
}

AppendFunc could have the signature (dst S, f func(E2) E1, src ...E2) S. Unlike lowercase append, it is always for working with a full slice—otherwise you could have just used append(s, f(v)). It also makes it easier to pass an anonymous mapping function when it's the last parameter.

Note that in-place map is CopyFunc(s, s, f) and new-slice map is AppendFunc([]T(nil), s, f).

AppendFunc could be written with existing functions (assuming xiter):

slices.AppendSeq(slices.Grow(dst, len(src)), xiter.Map(f, slices.Values(src)))

This is more awkward and unlikely to be as efficient. CopyFunc would require a corresponding CopySeq and would not be able to handle certain overlaps between dst and src correctly.

@gopherbot gopherbot added this to the Proposal milestone Jan 15, 2025
@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Jan 15, 2025
@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label Jan 15, 2025
@jimmyfrasche
Copy link
Member Author

As with all such additions it would be helpful to have numbers to see how common the issue is. It would be hard to classify all code that may be written in terms of one of these as they are quite general.

Personally I've written a lot of code like

for i := range s {
  s[i] = f(s2[i])
}

and

for _, v := range s {
  s2 = append(s2, f(v))
}

where f() is some function or method or conversion.

While there are a few variants of each to match against even with those, I think those would be a good proxy. They'd be a common case so they'd represent some large fraction of the true value.

@randall77
Copy link
Contributor

I think I'd rather see

for i := range s {
  s[i] = f(s2[i])
}

than

slices.CopyFunc(s, s2, f)

I think for 2 major reasons:

  1. The direction is clear. reading from s2, writing to s.
  2. The fact that f is a function and is being called is clear.

@jimmyfrasche
Copy link
Member Author

1 comes from the Copy part of the name and 2 comes from the Func part of the name.

@randall77
Copy link
Contributor

1 comes from the Copy part of the name

That requires remembering which direction the copy builtin goes :) Which I have messed up in the past. I've never messed up the direction of =.

2 comes from the Func part of the name.

It's hard to know exactly given just the name, without having to go read the docs. Without delving into the types of things one could imagine f is a filter, for example, instead of an element mapper.

@jimmyfrasche
Copy link
Member Author

copy and = go the same direction. It's only possible to mess up the direction if both arguments are the same type as slices.CopyFunc(ints, strs, strconv.Itoa) would fail to compile, so it's actually less of an issue than it is with regular copy.

Generally the function name would make it clear, as with Itoa above, and an inline function would be especially clear.

Maybe you would have to check the docs sometimes. That's fine. That's why they are there.

I don't think these are especially compelling arguments as they could be applied to lots of other things that already exist.

@jimmyfrasche
Copy link
Member Author

fwiw, the original names were either CopyMap/AppendMap or MapCopy/MapAppend which are slightly more evocative but ultimately Func is the naming scheme so there has to be a great justification to buck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal
Projects
Status: Incoming
Development

No branches or pull requests

4 participants