-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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
constraints: new package to define standard type parameter constraints #45458
Comments
I'd like to just quote this blogpost:
|
@gudvinr I don't think this proposal fails on either of those points. This package is named "constraints", and it contains type constraints. Nothing else. It doesn't contain a bunch of different APIs. But I would be happy to hear an alternative suggestion. Thanks. |
Given that these constraints seem to be centered around describing builtin types, is there a reason something like |
What factors decide which constraints are defined in this package? E.g., why are there all of |
@smasher164 Yes, as you say, @zephyrtronium I picked the constraints that seemed most likely to be useful. If you want to make the case for additional constraints to be defined by the package, this is the place to do it. |
I do think that the constraints I mentioned would be useful, but they might be better suited to living in packages This would leave the integer constraints in an awkward spot. They'll certainly be useful, but were we to try to place these constraints in relevant standard library packages, there doesn't seem to be an existing home for them. |
@zephyrtronium And I agree we should explore putting constraints in the stdlib differently from the way C++ provides generic algorithms and concepts in Although one might distinguish types and metatypes as being on different levels of some hierarchy, a user might expect the constraint to be in a package with related types and operations. A type that implements |
Your suggested interfaces only related to numeric types. So, when some developer will come up with another likely useful constraint for string manipulation he will probably put it into So it will either end up as |
I think we should add The same logic can be extended to the predeclared |
@komuw type Map[K comparable, V any] struct { ... }
// vs.
type Map[K constraints.Comparable, V constraints.Any] struct { ... } I also think that it'll be more confusing for new people if you have to explain why both Edit: Since the new idea is to think of them as sets of types as per #45346, maybe it would make more sense to name it something type set related to try to get it shortened a bit? I'm not sure what to name it, though. |
@gudvinr |
Having the constraints in one location promotes discoverability, but I agree with the other commenters that it feels more Go-like to keep them in relevant packages like |
Personally I continue to think that it makes more sense for to have a single constraints package that contains common constraints. I think it is simpler to write
then to write
I don't think it can just be |
Good point. I agree. But |
Why though? You can only use constraints in type parameters. So it does convey that it is a constraint. |
Not when looking at the doc; there they are interfaces. |
Their location doesn't restrict you from using |
Related to this, I think it is important to consider future language changes. If Go gets sum types that use type constraint syntax, then it no longer strictly makes sense to call the types in the proposed package just constraints. Contrarily, names like |
Maybe |
I think that if people are referring to constraints so frequently that typing We should expect that defining parameterized types and functions ought to be a lot less common than using them in real-world code that does useful things, and thus we need not optimize for code which excessively references the constraints package. |
I don't support this proposal as given; my suggestion would be to revisit it some time after generics are added to the language. A couple of releases later, there may be enough constraints in the wild to analyze which are the most useful and canonicalize them by adding them to the stdlib. Conjecture about what people will use or not use seems premature, because once in the stdlib it can't change. Wait for 5 packages to have a |
Certain kinds of generics are going to be annoying to write without this proposal. We can remove constraints if they don't seem useful, but I believe we at least need |
Ordered looks like this, in the old syntax:
That doesn’t seem atrocious to write in a package that needs it, does it? As an exercise, I’m curious generally how many functions you predict will need it? The example of Smallest is given in the proposal but you must be thinking of others. |
I once suggested the name
|
To me it's about readability. The parameterized function signatures will be shown in the documentation. The typographic bulkiness of the word |
I would be okay with an Analogous to the documented behaviour of NaN in |
It's unfortunate that the current spec already uses the term “ordered” for types that support I suppose that the built-in |
Is adding types in the future going to cause compatibility problems? For example: type MySigned interface { type int8, int16, int32, int64 }
func MyAbs[T MySigned](v T) T { ... }
func Abs[T constraints.Signed](v T) T { return MyAbs(v) } (Of course presumably Abs is in some other package, and perhaps the type lists involved are more complex.) That compiles today, but not in a future Go version where |
@benjaminjkraft I don't think it's fully clear under what circumstances it will be allowed to instantiate a generic function using a type parameter. Especially if #45346 is accepted. I could well imagine that we'll have to allow your example, as long as Either way, I think the ability to do this kind of extension seems important enough that we should make a stability exception in the form of a disclaimer for it. But it's definitely an interesting question. |
I propose one more type to be added:
This is useful for functions which can take integers, floats, and complex numbers as inputs, such as the quadratic formula:
(where sqrt has signature Another spelling could be EDIT - quadratic formula is a bit of a bad example for integers because of strange interactions with division and square roots... some simple examples that work well with all 3 “kinds” of numbers may be |
Right, one question I have no sense of is how likely this is to come up in practice. Obviously my example is a bit contrived, and I had trouble coming up with something that I'd actually expect to write, even across several packages of a large codebase. Another related example which seems much more likely to come up, especially if #45346 is accepted:
(I'm using syntax proposed in #45380 here for brevity, but since In general, I think the problem here is that experienced Go programmers know what it takes to make existing Go types backwards-compatible: for structs it's fine to add methods and fields (assuming no unkeyed literals); for interfaces may add methods only if they have an unexported method; for functions you just can't change them. But I, at least, don't know those rules for type lists yet -- these examples suggest that, more like interfaces, they can't be changed without some (in this case unknown) setup -- and I think we gotta figure them out to define the constraints package. |
This is blocked pending type sets, but we haven't forgotten about it. |
Type sets have been accepted, so we are ready to finalize this. If you have any outstanding concerns, please post them not here but on this discussion: #47319. Thanks! |
I have added |
Based on the discussion above, this proposal seems like a likely accept. |
There's a comment about chans in the discussion that we can work out in a separate issue. Otherwise, this seems good to go. |
No change in consensus, so accepted. 🎉 |
Change https://golang.org/cl/349709 mentions this issue: |
I don't understand the value of the Chan type here. What does it add in expressibility? Things like Signed and Unsigned are good, as they capture a wide range of types that are difficult to express. But what does Chan add? Map too. And Slice. These three all seem to just provide an alternate way to express something that is already very easy to write. |
They become useful when using constraint type inference. See how the slices proposal at #45955 uses |
I was worried that generic programming was going to spread throughout the library through these, which I suppose they still can (and probably will), but upon reflection I see what value these definitions add, which comes down to the difference between That's what I was missing, and I know I'm not polymorphic front and center but I find that subtle. Important, but the subtlety is something to keep in mind when explaining these things. You're bringing these ideas to a community not all of whom might be facile with all the subtleties involved. I am in that infacile subgroup. And it helps me to see things explained. The original post at the top is little more than a blob of code. It warranted explanation and justification. Instead it spends times on the future ( |
|
@randall77 We will introduce |
Change https://golang.org/cl/351853 mentions this issue: |
Change https://golang.org/cl/382535 mentions this issue: |
Change https://golang.org/cl/382460 mentions this issue: |
Note: Discussion is now at #47319
This proposal is for use with #43651. The proposal document for that issue mentions adding a constraints package to define some standard useful constraints. Here we propose a specific API to define. 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 intent is that if we add any new predeclared types, such as
int128
anduint128
, those types will be supported by these constraints as appropriate based on the constraint descriptions. To make this work, code should follow these guidelines: 1) given a type parameter whose constraint is taken from the constraints package, don't use that type parameter to instantiate a type/function whose constraint is not any and is not taken from the constraints package; 2) don't write a type switch to handle all types when using a constraint taken from the constraints package. We can add vet checks to detect cases where these guidelines are not followed.The text was updated successfully, but these errors were encountered: