diff options
| author | Ian Lance Taylor <iant@golang.org> | 2020-06-16 10:14:55 -0700 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2020-06-16 19:07:36 +0000 |
| commit | d44c4ded9c1a13dcf715ac641ce760170fbbcf64 (patch) | |
| tree | a9aab0b8e9f568a3489d6b7f214bb752163d8cc6 | |
| parent | ae6cad889430cb71a5f5fb6fb5b29c5e173741b2 (diff) | |
| download | go-x-proposal-d44c4ded9c1a13dcf715ac641ce760170fbbcf64.tar.xz | |
design: add go2draft-type-parameters.md
For golang/go#15292
Change-Id: I3c1dc1ffbc2e5df958f93dcb0d7a89a88f6b5107
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/238003
Reviewed-by: Robert Griesemer <gri@golang.org>
| -rw-r--r-- | design/go2draft-type-parameters.md | 3510 |
1 files changed, 3510 insertions, 0 deletions
diff --git a/design/go2draft-type-parameters.md b/design/go2draft-type-parameters.md new file mode 100644 index 0000000..d6ade68 --- /dev/null +++ b/design/go2draft-type-parameters.md @@ -0,0 +1,3510 @@ +# Type Parameters - Draft Design + +Ian Lance Taylor\ +Robert Griesemer\ +June 16, 2020 + +## Abstract + +We suggest extending the Go language to add optional type parameters +to types and functions. +Type parameters may be constrained by interface types. +We also suggest extending interface types, when used as type +constraints, to permit listing the set of types that may be assigned +to them. +Type inference via a unification algorithm is supported to permit +omitting type arguments from function calls in many cases. +The design is fully backward compatible with Go 1. + +## How to read this design draft + +This document is long. +Here is some guidance on how to read it. + +* We start with a high level overview, describing the concepts very + briefly. +* We then explain the full design starting from scratch, introducing + the details as we need them, with simple examples. +* After the design is completely described, we discuss implementation, + some issues with the design, and a comparison with other approaches + to generics. +* We then present several complete examples of how this design would + be used in practice. +* Following the examples some minor details are discussed in an + appendix. + +## Very high level overview + +This section explains the changes suggested by the design draft very +briefly. +This section is intended for people who are already familiar with how +generics would work in a language like Go. +These concepts will be explained in detail in the following sections. + +* Functions can have an additional type parameter list introduced by + the keyword `type`: `func F(type T)(p T) { ... }`. +* These type parameters can be used by the regular parameters and in + the function body. +* Types can also have a type parameter list: `type M(type T) []T`. +* Each type parameter can have an optional type constraint: `func + F(type T Constraint)(p T) { ... }` +* Type constraints are interface types. +* Interface types used as type constraints can have a list of + predeclared types; only types whose underlying type is one of those + types can implement the interface. +* Using a generic function or type requires passing type arguments. +* Type inference permits omitting the type arguments in common cases. +* If a type parameter has a type constraint its type argument must + implement the interface. +* Generic functions may only use operations permitted by the type + constraint. + +In the following sections we work through each of these language +changes in great detail. +You may prefer to skip ahead to the [examples](#Examples) to see what +generic code written to this design draft will look like in practice. + +## Background + +This version of the design draft has many similarities to the one +presented on July 31, 2019, but contracts have been removed and +replaced by interface types. + +There have been many [requests to add additional support for generic +programming](https://github.com/golang/go/wiki/ExperienceReports#generics) +in Go. +There has been extensive discussion on +[the issue tracker](https://golang.org/issue/15292) and on +[a living document](https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view). + +There have been several proposals for adding type parameters, which +can be found through the links above. +Many of the ideas presented here have appeared before. +The main new features described here are the syntax and the careful +examination of interface types as constraints. + +This design draft suggests extending the Go language to add a form of +parametric polymorphism, where the type parameters are bounded not by +a declared subtyping relationship (as in some object oriented +languages) but by explicitly defined structural constraints. + +This design does not support template metaprogramming or any other +form of compile time programming. + +As the term _generic_ is widely used in the Go community, we will +use it below as a shorthand to mean a function or type that takes type +parameters. +Don't confuse the term generic as used in this design with the same +term in other languages like C++, C#, Java, or Rust; they have +similarities but are not the same. + +## Design + +We will describe the complete design in stages based on simple +examples. + +### Type parameters + +Generic code is code that is written using types that will be +specified later. +An unspecified type is called a _type parameter_. +When running the generic code, the type parameter will be set to a +_type argument_. + +Here is a function that prints out each element of a slice, where the +element type of the slice, here called `T`, is unknown. +This is a trivial example of the kind of function we want to permit in +order to support generic programming. +(Later we'll also discuss [generic types](#Generic types)). + +```Go +// Print prints the elements of a slice. +// It should be possible to call this with any slice value. +func Print(s []T) { // Just an example, not the suggested syntax. + for _, v := range s { + fmt.Println(v) + } +} +``` + +With this approach, the first decision to make is: how should the type +parameter `T` be declared? +In a language like Go, we expect every identifier to be declared in +some way. + +Here we make a design decision: type parameters are similar to +ordinary non-type function parameters, and as such should be listed +along with other parameters. +However, type parameters are not the same as non-type parameters, so +although they appear in the list of parameters we want to distinguish +them. +That leads to our next design decision: we define an additional, +optional, parameter list, describing type parameters. +This parameter list appears before the regular parameters. +It starts with the keyword `type`, and lists type parameters. + +```Go +// Print prints the elements of any slice. +// Print has a type parameter T, and has a single (non-type) +// parameter s which is a slice of that type parameter. +func Print(type T)(s []T) { + // same as above +} +``` + +This says that within the function `Print` the identifier `T` is a +type parameter, a type that is currently unknown but that will be +known when the function is called. +As seen above, the type parameter may be used as a type when +describing the ordinary non-type parameters. +It may also be used within the body of the function. + +Since `Print` has a type parameter, any call of `Print` must provide a +type argument. +Later we will see how this type argument can usually be deduced from +the non-type argument, by using [function argument type +inference](#Function argument type inference). +For now, we'll pass the type argument explicitly. +Type arguments are passed much like type parameters are declared: as a +separate list of arguments. +At the call site, the `type` keyword is not used. + +```Go + // Call Print with a []int. + // Print has a type parameter T, and we want to pass a []int, + // so we pass a type argument of int by writing Print(int). + // The function Print(int) expects a []int as an argument. + + Print(int)([]int{1, 2, 3}) + + // This will print: + // 1 + // 2 + // 3 +``` + +### Constraints + +Let's make our example slightly more complicated. +Let's turn it into a function that converts a slice of any type into a +`[]string` by calling a `String` method on each element. + +```Go +// This function is INVALID. +func Stringify(type T)(s []T) (ret []string) { + for _, v := range s { + ret = append(ret, v.String()) // INVALID + } + return ret +} +``` + +This might seem OK at first glance, but in this example `v` has type +`T`, and we don't know anything about `T`. +In particular, we don't know that `T` has a `String` method. +So the call to `v.String()` is invalid. + +Naturally, the same issue arises in other languages that support +generic programming. +In C++, for example, a generic function (in C++ terms, a function +template) can call any method on a value of generic type. +That is, in the C++ approach, calling `v.String()` is fine. +If the function is called with a type argument that does not have a +`String` method, the error is reported when compiling the call to +`v.String` with that type argument. +These errors can be lengthy, as there may be several layers of generic +function calls before the error occurs, all of which must be reported +to understand what went wrong. + +The C++ approach would be a poor choice for Go. +One reason is the style of the language. +In Go we don't refer to names, such as, in this case, `String`, and +hope that they exist. +Go resolves all names to their declarations when they are seen. + +Another reason is that Go is designed to support programming at +scale. +We must consider the case in which the generic function definition +(`Stringify`, above) and the call to the generic function (not shown, +but perhaps in some other package) are far apart. +In general, all generic code expects the type arguments to meet +certain requirements. +We refer to these requirements as _constraints_ (other languages have +similar ideas known as type bounds or trait bounds or concepts). +In this case, the constraint is pretty obvious: the type has to have a +`String() string` method. +In other cases it may be much less obvious. + +We don't want to derive the constraints from whatever `Stringify` +happens to do (in this case, call the `String` method). +If we did, a minor change to `Stringify` might change the +constraints. +That would mean that a minor change could cause code far away, that +calls the function, to unexpectedly break. +It's fine for `Stringify` to deliberately change its constraints, and +force users to change. +What we want to avoid is `Stringify` changing its constraints +accidentally. + +This means that the constraints must set limits on both the type +arguments passed by the caller and the code in the generic function. +The caller may only pass type arguments that satisfy the constraints. +The generic function may only use those values in ways that are +permitted by the constraints. +This is an important rule that we believe should apply to any attempt +to define generic programming in Go: generic code can only use +operations that its type arguments are known to implement. + +### Operations permitted for any type + +Before we discuss constraints further, let's briefly note what happens +in their absence. +If a generic function does not specify a constraint for a type +parameter, as is the case for the `Print` method above, then any type +argument is permitted for that parameter. +The only operations that the generic function can use with values of +that type parameter are those operations that are permitted for values +of any type. +In the example above, the `Print` function declares a variable `v` +whose type is the type parameter `T`, and it passes that variable to a +function. + +The operations permitted for any type are: + +* declare variables of those types +* assign other values of the same type to those variables +* pass those variables to functions or return them from functions +* take the address of those variables +* convert or assign values of those types to the type `interface{}` +* convert a value of type `T` to type `T` (permitted but useless) +* use a type assertion to convert an interface value to the type +* use the type as a case in a type switch +* define and use composite types that use those types, such as a slice + of that type +* pass the type to some builtin functions such as `new` + +It's possible that future language changes will add other such +operations, though none are currently anticipated. + +### Defining constraints + +Go already has a construct that is close to what we need for a +constraint: an interface type. +An interface type is a set of methods. +The only values that can be assigned to a variable of interface type +are those whose types implement the same methods. +The only operations that can be done with a value of interface type, +other than operations permitted for any type, are to call the +methods. + +Calling a generic function with a type argument is similar to +assigning to a variable of interface type: the type argument must +implement the constraints of the type parameter. +Writing a generic function is like using values of interface type: the +generic code can only use the operations permitted by the constraint +(or operations that are permitted for any type). + +In this design, constraints are simply interface types. +Implementing a constraint is simply implementing the interface type. +(Later we'll see how to define constraints for operations other than +method calls, such as [binary operators](#Operators)). + +For the `Stringify` example, we need an interface type with a `String` +method that takes no arguments and returns a value of type `string`. + +```Go +// Stringer is a type constraint that requires the type argument to have +// a String method and permits the generic function to call String. +// The String method should return a string representation of the value. +type Stringer interface { + String() string +} +``` + +(It doesn't matter for this discussion, but this defines the same +interface as the standard library's `fmt.Stringer` type, and real +code would likely simply use `fmt.Stringer`.) + +### Using a constraint + +For a generic function, a constraint can be thought of as the type of +the type argument: a meta-type. +So, although generic functions are not required to use constraints, +when they do they are listed in the type parameter list as the +meta-type of a type parameter. + +```Go +// Stringify calls the String method on each element of s, +// and returns the results. +func Stringify(type T Stringer)(s []T) (ret []string) { + for _, v := range s { + ret = append(ret, v.String()) + } + return ret +} +``` + +The single type parameter `T` is followed by the constraint that +applies to `T`, in this case `Stringer`. + +### Multiple type parameters + +Although the `Stringify` example uses only a single type parameter, +functions may have multiple type parameters. + +```Go +// Print2 has two type parameters and two non-type parameters. +func Print2(type T1, T2)(s1 []T1, s2 []T2) { ... } +``` + +Compare this to + +```Go +// Print2Same has one type parameter and two non-type parameters. +func Print2Same(type T)(s1 []T, s2 []T) { ... } +``` + +In `Print2` `s1` and `s2` may be slices of different types. +In `Print2Same` `s1` and `s2` must be slices of the same element +type. + +Each type parameter may have its own constraint. + +```Go +// Stringer is a type constraint that requires a String method. +// The String method should return a string representation of the value. +type Stringer interface { + String() string +} + +// Plusser is a type constraint that requires a Plus method. +// The Plus method is expected to add the argument to an internal +// string and return the result. +type Plusser interface { + Plus(string) string +} + +// ConcatTo takes a slice of elements with a String method and a slice +// of elements with a Plus method. The slices should have the same +// number of elements. This will convert each element of s to a string, +// pass it to the Plus method of the corresponding element of p, +// and return a slice of the resulting strings. +func ConcatTo(type S Stringer, P Plusser)(s []S, p []P) []string { + r := make([]string, len(s)) + for i, v := range s { + r[i] = p[i].Plus(v.String()) + } + return r +} +``` + +If a constraint is specified for any type parameter, every type +parameter must have a constraint. +If some type parameters need a constraint and some do not, those that +do not should have a constraint of `interface{}`. + +```Go +// StrAndPrint takes a slice of labels, which can be any type, +// and a slice of values, which must have a String method, +// converts the values to strings, and prints the labelled strings. +func StrAndPrint(type L interface{}, T Stringer)(labels []L, vals []T) { + // Stringify was defined above. It returns a []string. + for i, s := range Stringify(vals) { + fmt.Println(labels[i], s) + } +} +``` + +A single constraint can be used for multiple type parameters, just as +a single type can be used for multiple non-type function parameters. +The constraint applies to each type parameter separately. + +```Go +// Stringify2 converts two slices of different types to strings, +// and returns the concatenation of all the strings. +func Stringify2(type T1, T2 Stringer)(s1 []T1, s2 []T2) string { + r := "" + for _, v1 := range s1 { + r += v1.String() + } + for _, v2 := range s2 { + r += v2.String() + } + return r +} +``` + +### Generic types + +We want more than just generic functions: we also want generic types. +We suggest that types be extended to take type parameters. + +```Go +// Vector is a name for a slice of any element type. +type Vector(type T) []T +``` + +A type's parameters are just like a function's type parameters. + +Within the type definition, the type parameters may be used like any +other type. + +To use a generic type, you must supply type arguments. +This looks like a function call, except that the function in this case +is actually a type. +This is called _instantiation_. +When we instantiate a type by supplying type arguments for the type +parameters, we produce a type in which each use of a type parameter in +the type definition is replaced by the corresponding type argument. + +```Go +// v is a Vector of int values. +// +// This is similar to pretending that "Vector(int)" is a valid identifier, +// and writing +// type "Vector(int)" []int +// var v "Vector(int)" +// All uses of Vector(int) will refer to the same "Vector(int)" type. +// +var v Vector(int) +``` + +Generic types can have methods. +The receiver type of a method must declare the same number of type +parameters as are declared in the receiver type's definition. +They are declared without the `type` keyword or any constraint. + +```Go +// Push adds a value to the end of a vector. +func (v *Vector(T)) Push(x T) { *v = append(*v, x) } +``` + +The type parameters listed in a method declaration need not have the +same names as the type parameters in the type declaration. +In particular, if they are not used by the method, they can be `_`. + +A generic type can refer to itself in cases where a type can +ordinarily refer to itself, but when it does so the type arguments +must be the type parameters, listed in the same order. +This restriction prevents infinite recursion of type instantiation. + +```Go +// List is a linked list of values of type T. +type List(type T) struct { + next *List(T) // this reference to List(T) is OK + val T +} + +// This type is INVALID. +type P(type T1, T2) struct { + F *P(T2, T1) // INVALID; must be (T1, T2) +} +``` + +This restriction applies to both direct and indirect references. + +```Go +// ListHead is the head of a linked list. +type ListHead(type T) struct { + head *ListElement(T) +} + +// ListElement is an element in a linked list with a head. +// Each element points back to the head. +type ListElement(type T) struct { + next *ListElement(T) + val T + // Using ListHead(T) here is OK. + // ListHead(T) refers to ListElement(T) refers to ListHead(T). + // Using ListHead(int) would not be OK, as ListHead(T) + // would have an indirect reference to ListHead(int). + head *ListHead(T) +} +``` + +(Note: with more understanding of how people want to write code, it +may be possible to relax this rule to permit some cases that use +different type arguments.) + +The type parameter of a generic type may have constraints. + +```Go +// StringableVector is a slice of some type, where the type +// must have a String method. +type StringableVector(type T Stringer) []T + +func (s StringableVector(T)) String() string { + var sb strings.Builder + for i, v := range s { + if i > 0 { + sb.WriteString(", ") + } + // It's OK to call v.String here because v is of type T + // and T's constraint is Stringer. + sb.WriteString(v.String()) + } + return sb.String() +} +``` + +### 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. + +This is not a fundamental restriction but it complicates the language +specification and the implementation. + +(Note: this is a feature that can perhaps be added later if it proves +necessary.) + +### Operators + +As we've seen, we are using interface types as constraints. +Interface types provide a set of methods, and nothing else. +This means that with what we've seen so far, the only thing that +generic functions can do with values of type parameters, other than +operations that are permitted for any type, is call methods. + +However, method calls are not sufficient for everything we want to +express. +Consider this simple function that returns the smallest element of a +slice of values, where the slice is assumed to be non-empty. + +```Go +// This function is INVALID. +func Smallest(type T)(s []T) T { + r := s[0] // panic if slice is empty + for _, v := range s[1:] { + if v < r { // INVALID + r = v + } + } + return r +} +``` + +Any reasonable generics implementation should let you write this +function. +The problem is the expression `v < r`. +This assumes that `T` supports the `<` operator, but `T` has no +constraint. +Without a constraint the function `Smallest` can only use operations +that are available for all types, but not all Go types support `<`. +Unfortunately, since `<` is not a method, there is no obvious way to +write a constraint—an interface type—that permits `<`. + +We need a way to write a constraint that accepts only types that +support `<`. +In order to do that, we observe that, aside from two exceptions that +we will discuss later, all the arithmetic, comparison, and logical +operators defined by the language may only be used with types that are +predeclared by the language, or with defined types whose underlying +type is one of those predeclared types. +That is, the operator `<` can only be used with a predeclared type +such as `int` or `float64`, or a defined type whose underlying type is +one of those types. +Go does not permit using `<` with a composite type or with an +arbitrary defined type. + +This means that rather than try to write a constraint for `<`, we can +approach this the other way around: instead of saying which operators +a constraint should support, we can say which (underlying) types a +constraint should accept. + +#### Type lists in constraints + +An interface type used as a constraint may list explicit types that +may be used as type arguments. +This is done using the `type` keyword followed by a comma-separated +list of types. + +For example: + +```Go +// SignedInteger is a type constraint that permits any +// signed integer type. +type SignedInteger interface { + type int, int8, int16, int32, int64 +} +``` + +The `SignedInteger` constraint specifies that the type argument +must be one of the listed types. +More precisely, the underlying type of the type argument must be +identical to the underlying type of one of the types in the type +list. +This means that `SignedInteger` will accept the listed integer types, +and will also accept any type that is defined as one of those types. + +When a generic function uses a type parameter with one of these +constraints, it may use any operation that is permitted by all of the +listed types. +This can be an operation like `<`, `range`, `<-`, and so forth. +If the function can be compiled successfully using each type listed in +the constraint, then the operation is permitted. + +A constraint may only have one type list. + +For the `Smallest` example shown earlier, we could use a constraint +like this: + +```Go +package constraints + +// Ordered is a type constraint that matches any ordered type. +// An ordered type is one that supports the <, <=, >, and >= operators. +type Ordered interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64, + string +} +``` + +In practice this constraint would likely be defined and exported in a +new standard library package, `constraints`, so that it could be used +by function and type definitions. + +Given that constraint, we can write this function, now valid: + +```Go +// Smallest returns the smallest element in a slice. +// It panics if the slice is empty. +func Smallest(type T constraints.Ordered)(s []T) T { + r := s[0] // panics if slice is empty + for _, v := range s[1:] { + if v < r { + r = v + } + } + return r +} +``` + +#### Comparable types in constraints + +Earlier we mentioned that there are two exceptions to the rule that +operators may only be used with types that are predeclared by the +language. +The exceptions are `==` and `!=`, which are permitted for struct, +array, and interface types. +These are useful enough that we want to be able to write a constraint +that accepts any comparable type. + +To do this we introduce a new predeclared type constraint: +`comparable`. +A type parameter with the `comparable` constraint accepts as a type +argument any comparable type. +It permits the use of `==` and `!=` with values of that type parameter. + +For example, this function may be instantiated with any comparable +type: + +```Go +// Index returns the index of x in s, or -1 if not found. +func Index(type T comparable)(s []T, x T) int { + for i, v := range s { + // v and x are type T, which has the comparable + // constraint, so we can use == here. + if v == x { + return i + } + } + return -1 +} +``` + +Since `comparable`, like all constraints, is an interface type, it can +be embedded in another interface type used as a constraint: + +```Go +// ComparableHasher is a type constraint that matches all +// comparable types with a Hash method. +type ComparableHasher interface { + comparable + Hash() uintptr +} +``` + +The constraint `ComparableHasher` is implemented by any type that is +comparable and also has a `Hash() uintptr` method. +A generic function that uses `ComparableHasher` as a constraint can +compare values of that type and can call the `Hash` method. + +#### Type lists in interface types + +Interface types with type lists may only be used as constraints on +type parameters. +They may not be used as ordinary interface types. +The same is true of the predeclared interface type `comparable`. + +This restriction may be lifted in future language versions. +An interface type with a type list may be useful as a form of sum +type, albeit one that can have the value `nil`. +Some alternative syntax would likely be required to match on identical +types rather than on underlying types; perhaps `type ==`. +For now, this is not permitted. + +### Function argument type inference + +In many cases, when calling a function with type parameters, we can +use type inference to avoid having to explicitly write out the type +arguments. + +Go back to [the example](#Type parameters) of a call to the simple +`Print` function: + +```Go + Print(int)([]int{1, 2, 3}) +``` + +The type argument `int` in the function call can be inferred from the +type of the non-type argument. + +This can only be done when all the function's type parameters are used +for the types of the function's (non-type) input parameters. +If there are some type parameters that are used only for the +function's result parameter types, or only in the body of the +function, then our algorithm does not infer the type arguments for the +function, since there is no value from which to infer the types. + +When the function's type arguments can be inferred, the language uses +type unification. +On the caller side we have the list of types of the actual (non-type) +arguments, which for the `Print` example is simply `[]int`. +On the function side is the list of the types of the function's +non-type parameters, which for `Print` is `[]T`. +In the lists, we discard respective arguments for which the function +side does not use a type parameter. +We must then unify the remaining argument types. + +Type unification is a two-pass algorithm. +In the first pass, we ignore untyped constants on the caller side and +their corresponding types in the function definition. + +We compare corresponding types in the lists. +Their structure must be identical, except that type parameters on the +function side match the type that appears on the caller side at the +point where the type parameter occurs. +If the same type parameter appears more than once on the function +side, it will match multiple argument types on the caller side. +Those caller types must be identical, or type unification fails, and +we report an error. + +After the first pass, we check any untyped constants on the caller +side. +If there are no untyped constants, or if the type parameters in the +corresponding function types have matched other input types, then +type unification is complete. + +Otherwise, for the second pass, for any untyped constants whose +corresponding function types are not yet set, we determine the default +type of the untyped constant in [the usual +way](https://golang.org/ref/spec#Constants). +Then we run the type unification algorithm again, this time with no +untyped constants. + +In this example + +```Go + s1 := []int{1, 2, 3} + Print(s1) +``` + +we compare `[]int` with `[]T`, match `T` with `int`, and we are done. +The single type parameter `T` is `int`, so we infer that the call to +`Print` is really a call to `Print(int)`. + +For a more complex example, consider + +```Go +// Map calls the function f on every element of the slice s, +// returning a new slice of the results. +func Map(type F, T)(s []F, f func(F) T) []T { + r := make([]T, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} +``` + +The two type parameters `F` and `T` are both used for input +parameters, so type inference is possible. +In the call + +```Go + strs := Map([]int{1, 2, 3}, strconv.Itoa) +``` + +we unify `[]int` with `[]F`, matching `F` with `int`. +We unify the type of `strconv.Itoa`, which is `func(int) string`, +with `func(F) T`, matching `F` with `int` and `T` with `string`. +The type parameter `F` is matched twice, both times with `int`. +Unification succeeds, so the call written as `Map` is a call of +`Map(int, string)`. + +To see the untyped constant rule in effect, consider: + +```Go +// NewPair returns a pair of values of the same type. +func NewPair(type F)(f1, f2 F) *Pair(F) { ... } +``` + +In the call `NewPair(1, 2)` both arguments are untyped constants, so +both are ignored in the first pass. +There is nothing to unify. +We still have two untyped constants after the first pass. +Both are set to their default type, `int`. +The second run of the type unification pass unifies `F` with +`int`, so the final call is `NewPair(int)(1, 2)`. + +In the call `NewPair(1, int64(2))` the first argument is an untyped +constant, so we ignore it in the first pass. +We then unify `int64` with `F`. +At this point the type parameter corresponding to the untyped constant +is fully determined, so the final call is `NewPair(int64)(1, +int64(2))`. + +In the call `NewPair(1, 2.5)` both arguments are untyped constants, +so we move on the second pass. +This time we set the first constant to `int` and the second to +`float64`. +We then try to unify `F` with both `int` and `float64`, so unification +fails, and we report a compilation error. + +Note that type inference is done without regard to constraints. +First we use type inference to determine the type arguments to use for +the function, and then, if that succeeds, we check whether those type +arguments implement the constraints (if any). + +Note that after successful type inference, the compiler must still +check that the arguments can be assigned to the parameters, as for any +function call. + +(Note: type inference is a convenience feature. +Although we think it is an important feature, it does not add any +functionality to the design, only convenience in using it. +It would be possible to omit it from the initial implementation, and +see whether it seems to be needed. +That said, this feature doesn't require additional syntax, and +produces more readable code.) + +### Using types that refer to themselves in constraints + +It can be useful for a generic function to require a type argument +with a method whose argument is the type itself. +For example, this arises naturally in comparison methods. +(Note that we are talking about methods here, not operators.) +Suppose we want to write an `Index` method that uses an `Equal` method +to check whether it has found the desired value. +We would like to write that like this: + +```Go +// Index returns the index of e in s, or -1 if not found. +func Index(type T Equaler)(s []T, e T) int { + for i, v := range s { + if e.Equal(v) { + return i + } + } + return -1 +} +``` + +In order to write the `Equaler` constraint, we have to write a +constraint that can refer to the type argument being passed in. +There is no way to do that directly, but what we can do is write an +interface type that use a type parameter. + +```Go +// Equaler is a type constraint for types with an Equal method. +type Equaler(type T) interface { + Equal(T) bool +} +``` + +To make this work, `Index` will pass `T` as the type argument to +`Equaler`. +The rule is that if a type contraint has a single type parameter, and +it is used in a function's type parameter list without an explicit +type argument, then the type argument is the type parameter being +constrained. +In other words, in the definition of `Index` above, the constraint +`Equaler` is treated as `Equaler(T)`. + +This version of `Index` would be used with a type like `equalInt` +defined here: + +```Go +// equalInt is a version of int that implements Equaler. +type equalInt int + +// The Equal method lets equalInt implement the Equaler constraint. +func (a equalInt) Equal(b equalInt) bool { return a == b } + +// indexEqualInts returns the index of e in s, or -1 if not found. +func indexEqualInt(s []equalInt, e equalInt) int { + return Index(equalInt)(s, e) +} +``` + +In this example, when we pass `equalInt` to `Index`, we check whether +`equalInt` implements the constraint `Equaler`. +Since `Equaler` has a type parameter, we pass the type argument of +`Index`, which is `equalInt`, as the type argument to `Equaler`. +The constraint is, then, `Equaler(equalInt)`, which is satisfied +by any type with a method `Equal(equalInt) bool`. +The `equalInt` type has a method `Equal` that accepts a parameter of +type `equalInt`, so all is well, and the compilation succeeds. + +### Mutually referencing type parameters + +Within a single type parameter list, constraints may refer to any of +the other type parameters, even ones that are declared later in the +same list. +(The scope of a type parameter starts at the `type` keyword of the +parameter list and extends to the end of the enclosing function or +type declaration.) + +For example, consider a generic graph package that contains generic +algorithms that work with graphs. +The algorithms use two types, `Node` and `Edge`. +`Node` is expected to have a method `Edges() []Edge`. +`Edge` is expected to have a method `Nodes() (Node, Node)`. +A graph can be represented as a `[]Node`. + +This simple representation is enough to implement graph algorithms +like finding the shortest path. + +```Go +package graph + +// NodeConstraint is the type constraint for graph nodes: +// they must have an Edges method that returns the Edge's +// that connect to this Node. +type NodeConstraint(type Edge) interface { + Edges() []Edge +} + +// EdgeConstraint is the type constraint for graph edges: +// they must have a Nodes method that returns the two Nodes +// that this edge connects. +type EdgeConstraint(type Node) interface { + Nodes() (from, to Node) +} + +// Graph is a graph composed of nodes and edges. +type Graph(type Node NodeConstraint(Edge), Edge EdgeConstraint(Node)) struct { ... } + +// New returns a new graph given a list of nodes. +func New( + type Node NodeConstraint(Edge), Edge EdgeConstraint(Node)) ( + nodes []Node) *Graph(Node, Edge) { + ... +} + +// ShortestPath returns the shortest path between two nodes, +// as a list of edges. +func (g *Graph(Node, Edge)) ShortestPath(from, to Node) []Edge { ... } +``` + +There are a lot of type arguments and instantiations here. +In the constraint on `Node` in `Graph`, the `Edge` being passed to the +type constraint `NodeConstraint` is the second type parameter of +`Graph`. +This instantiates `NodeConstraint` with the type parameter `Edge`, so +we see that `Node` must have a method `Edges` that returns a slice of +`Edge`, which is what we want. +The same applies to the constraint on `Edge`, and the same type +parameters and constraints are repeated for the function `New`. +We aren't claiming that this is simple, but we are claiming that it is +possible. + +It's worth noting that while at first glance this may look like a +typical use of interface types, `Node` and `Edge` are non-interface +types with specific methods. +In order to use `graph.Graph`, the type arguments used for `Node` and +`Edge` have to define methods that follow a certain pattern, but they +don't have to actually use interface types to do so. +In particular, the methods do not return interface types. + +For example, consider these type definitions in some other package: + +```Go +// Vertex is a node in a graph. +type Vertex struct { ... } + +// Edges returns the edges connected to v. +type (v *Vertex) Edges() []*FromTo { ... } + +// FromTo is an edge in a graph. +type FromTo struct { ... } + +// Nodes returns the nodes that ft connects. +func (ft *FromTo) Nodes() (*Vertex, *Vertex) { ... } +``` + +There are no interface types here, but we can instantiate +`graph.Graph` using the type arguments `*Vertex` and `*FromTo`. + +```Go +var g = graph.New(*Vertex, *FromTo)([]*Vertex{ ... }) +``` + +`*Vertex` and `*FromTo` are not interface types, but when used +together they define methods that implement the constraints of +`graph.Graph`. +Note that we couldn't pass plain `Vertex` or `FromTo` to `graph.New`, +since `Vertex` and `FromTo` do not implement the constraints. +The `Edges` and `Nodes` methods are defined on the pointer types +`*Vertex` and `*FromTo`; the types `Vertex` and `FromTo` do not have +any methods. + +When we use a generic interface type as a constraint, we first +instantiate the type with the type argument(s) supplied in the type +parameter list, and then compare the corresponding type argument +against the instantiated constraint. +In this example, the `Node` type argument to `graph.New` has a +constraint `NodeConstraint(Edge)`. +When we call `graph.New` with a `Node` type argument of `*Vertex` and +a `Edge` type argument of `*FromTo`, in order to check the constraint +on `Node` the compiler instantiates `NodeConstraint` with the type +argument `*FromTo`. +That produces an instantiated constraint, in this case a requirement +that `Node` have a method `Edges() []*FromTo`, and the compiler +verifies that `*Vertex` satisfies that constraint. + +Although `Node` and `Edge` do not have to be instantiated with +interface types, it is also OK to use interface types if you like. + +```Go +type NodeInterface interface { Edges() []EdgeInterface } +type EdgeInterface interface { Nodes() (NodeInterface, NodeInterface) } +``` + +We could instantiate `graph.Graph` with the types `NodeInterface` and +`EdgeInterface`, since they implement the type constraints. +There isn't much reason to instantiate a type this way, but it is +permitted. + +This ability for type parameters to refer to other type parameters +illustrates an important point: it should be a requirement for any +attempt to add generics to Go that it be possible to instantiate +generic code with multiple type arguments that refer to each other in +ways that the compiler can check. + +### Pointer methods + +There are cases where a generic function will only work as expected if +a type argument `A` has methods defined on the pointer type `*A`. +This happens when writing a generic function that expects to call a +method that modifies a value. + +Consider this example of a function that expects a type `T` that has a +`Set(string)` method that initializes the value based on a string. + +```Go +// Setter is a type constraint that requires that the type +// implement a Set method that sets the value from a string. +type Setter interface { + Set(string) +} + +// FromStrings takes a slice of strings and returns a slice of T, +// calling the Set method to set each returned value. +// +// Note that because T is only used for a result parameter, +// type inference does not work when calling this function. +// The type argument must be passed explicitly at the call site. +// +// This example compiles but is unlikely to work as desired. +func FromStrings(type T Setter)(s []string) []T) { + result := make([]T, len(s)) + for i, v := range s { + result[i].Set(v) + } + return result +} +``` + +Now let's see some code in a different package (this example is +invalid). + +```Go +// Settable is a integer type that can be set from a string. +type Settable int + +// Set sets the value of *p from a string. +func (p *Settable) Set(s string) { + i, _ := strconv.Atoi(s) // real code should not ignore the error + *p = Settable(i) +} + +func F() { + // INVALID + nums := FromStrings(Settable)([]string{"1", "2"}) + // Here we want nums to be []Settable{1, 2}. + ... +} +``` + +The goal is to use `FromStrings` to get a slice of type `[]Settable`. +Unfortunately, this example is not valid and will not compile. + +The problem is that `FromStrings` requires a type that has a +`Set(string)` method. +The function `F` is trying to instantiate `FromStrings` with +`Settable`, but `Settable` does not have a `Set` method. +The type that has a `Set` method is `*Settable`. + +So let's rewrite `F` to use `*Settable` instead. + +```Go +func F() { + // Compiles but does not work as desired. + // This will panic at run time when calling the Set method. + nums := FromStrings(*Settable)([]string{"1", "2"}) + ... +} +``` + +This compiles but unfortunately it will panic at run time. +The problem is that `FromStrings` creates a slice of type `[]T`. +When instantiated with `*Settable`, that means a slice of type +`[]*Settable`. +When `FromStrings` calls `result[i].Set(v)`, that passes the pointer +stored in `result[i]` to the `Set` method. +That pointer is `nil`. +The `Settable.Set` method will be invoked with a `nil` receiver, +and will raise a panic due to a `nil` dereference error. + +What we need is a way to write `FromStrings` such that it can take +the type `Settable` as an argument but invoke a pointer method. +To repeat, we can't use `Settable` because it doesn't have a `Set` +method, and we can't use `*Settable` because then we can't create a +slice of type `Settable`. + +One approach that could work would be to use two different type +parameters: both `Settable` and `*Settable`. + +```Go +package from + +// Setter2 is a type constraint that requires that the type +// implement a Set method that sets the value from a string, +// and also requires that the type be a pointer to its type parameter. +type Setter2(type B) interface { + Set(string) + type *B +} + +// FromStrings2 takes a slice of strings and returns a slice of T, +// calling the Set method to set each returned value. +// +// We use two different type parameters so that we can return +// a slice of type T but call methods on *T. +// The Setter2 constraint ensures that PT is a pointer to T. +func FromStrings2(type T interface{}, PT Setter2(T))(s []string) []T { + result := make([]T, len(s)) + for i, v := range s { + // The type of &result[i] is *T which is in the type list + // of Setter2, so we can convert it to PT. + p := PT(&result[i]) + // PT has a Set method. + p.Set(v) + } + return result +} +``` + +We would call `FromStrings2` like this: + +```Go +func F2() { + // FromStrings2 takes two type parameters. + // The second parameter must be a pointer to the first. + // Settable is as above. + nums := FromStrings2(Settable, *Settable)([]string{"1", "2"}) + // Now nums is []Settable{1, 2}. + ... +} +``` + +This approach works as expected, but it is awkward. +It forces `F2` to work around a problem in `FromStrings2` by passing +two type arguments. +The second type argument is required to be a pointer to the first type +argument. +This is a complex requirement for what seems like it ought to be a +reasonably simple case. + +Another approach would be to pass in a function rather than calling a +method. + +```Go +// FromStrings3 takes a slice of strings and returns a slice of T, +// calling the set function to set each returned value. +func FromStrings3(type T)(s []string, set func(*T, string)) []T { + results := make([]T, len(s)) + for i, v := range s { + set(&results[i], v) + } + return results +} +``` + +We would call `Strings3` like this: + +```Go +func F3() { + // FromStrings3 takes a function to set the value. + // Settable is as above. + nums := FromStrings3(Settable)([]string{"1", "2"}, + func(p *Settable, s string) { p.Set(s) }) + // Now nums is []Settable{1, 2}. +} +``` + +This approach also works as expected, but it is also awkward. +The caller has to pass in a function just to call the `Set` method. +This is the kind of boilerplate code that we would hope to avoid when +using generics. + +Although these approaches are awkward, they do work. +That said, we suggest another feature to address this kind of issue: a +way to express constraints on the pointer to the type parameter, +rather than on the type parameter itself. +The way to do this is to write the type parameter as though it were a +pointer type: `(type *T Constraint)`. + +Writing `*T` instead of `T` in a type parameter list changes two +things. +Let's assume that the type argument at the call site is `A`, and the +constraint is `Constraint` (this syntax may be used without a +constraint, but there is no reason to do so). + +The first thing that changes is that `Constraint` is applied to `*A` +rather than `A`. +That is, `*A` must implement `Constraint`. +It's OK if `A` implements `Constraint`, but the requirement is that +`*A` implement it. +Note that if `Constraint` has any methods, this implies that `A` must +not be a pointer type: if `A` is a pointer type, then `*A` is a +pointer to a pointer, and such types never have any methods. + +The second thing that changes is that within the body of the function, +any methods in `Constraint` are treated as though they were pointer +methods. +They may only be invoked on values of type `*T` or addressable values +of type `T`. + +```Go +// FromStrings takes a slice of strings and returns a slice of T, +// calling the Set method to set each returned value. +// +// We write *T, meaning that given a type argument A, +// the pointer type *A must implement Setter. +// +// Note that because T is only used for a result parameter, +// type inference does not work when calling this function. +// The type argument must be passed explicitly at the call site. +func FromStrings(type *T Setter)(s []string) []T { + result := make([]T, len(s)) + for i, v := range s { + // result[i] is an addressable value of type T, + // so it's OK to call Set. + result[i].Set(v) + } + return result +} +``` + +Again, using `*T` here means that given a type argument `A`, the type +`*A` must implement the constraint `Setter`. +In this case, `Set` must be in the method set of `*A`. +Within `FromStrings`, using `*T` means that the `Set` method may only +be called on an addressable value of type `T`. + +We can now use this as + +```Go +func F() { + // With the rewritten FromStrings, this is now OK. + // *Settable implements Setter. + nums := from.Strings(Settable)([]string{"1", "2"}) + // Here nums is []Settable{1, 2}. + ... +} +``` + +To be clear, using `type *T Setter` does not mean that the `Set` +method must only be a pointer method. +`Set` could still be a value method. +That would be OK because all value methods are also in the pointer +type's method set. +In this example that only makes sense if `Set` can be written as a +value method, which might be the case when defining the method on a +struct that contains pointer fields. + +### Using generic types as unnamed function parameter types + +When parsing an instantiated type as an unnamed function parameter +type, there is a parsing ambiguity. + +```Go +var f func(x(T)) +``` + +In this example we don't know whether the function has a single +unnamed parameter of the instantiated type `x(T)`, or whether this is +a named parameter `x` of the type `(T)` (written with parentheses). + +We would prefer that this mean the former: an unnamed parameter of the +instantiated type `x(T)`. +This is not actually backward compatible with the current language, +where it means the latter. +However, the gofmt program currently rewrites `func(x(T))` to `func(x +T)`, so `func(x(T))` is very unusual in plain Go code. + +Therefore, we propose that the language change so that `func(x(T))` +now means a single parameter of type `x(T)`. +This will potentially break some existing programs, but the fix will +be to simply run gofmt. +This will potentially change the meaning of programs that write +`func(x(T))`, that don't use gofmt, and that choose to introduce a +generic type `x` with the same name as a function parameter with a +parenthesized type. +We believe that such programs will be exceedingly rare. + +Still, this is a risk, and if the risk seems too large we can avoid +making this change. + +### Values of type parameters are not boxed + +In the current implementations of Go, interface values always hold +pointers. +Putting a non-pointer value in an interface variable causes the value +to be _boxed_. +That means that the actual value is stored somewhere else, on the heap +or stack, and the interface value holds a pointer to that location. + +In this design, values of generic types are not boxed. +For example, let's look back at our earlier example of +`from.Strings`. +When it is instantiated with type `Settable`, it returns a value of +type `[]Settable`. +For example, we can write + +```Go +// Settable is an integer type that can be set from a string. +type Settable int + +// Set sets the value of *p from a string. +func (p *Settable) Set(s string) (err error) { + // same as above +} + +func F() { + // The type of nums is []Settable. + nums, err := from.Strings(Settable)([]string{"1", "2"}) + if err != nil { ... } + // Settable can be converted directly to int. + // This will set first to 1. + first := int(nums[0]) + ... +} +``` + +When we call `from.Strings` with the type `Settable` we get back a +`[]Settable` (and an error). +The elements of that slice will be `Settable` values, which is to say, +they will be integers. +They will not be boxed, even though they were created and set by a +generic function. + +Similarly, when a generic type is instantiated it will have the +expected types as components. + +```Go +type Pair(type F1, F2) struct { + first F1 + second F2 +} +``` + +When this is instantiated, the fields will not be boxed, and no +unexpected memory allocations will occur. +The type `Pair(int, string)` is convertible to `struct { first int; +second string }`. + +### More on type lists + +Let's return now to type lists to cover some less important details +that are still worth noting. +These are not additional rules or concepts, but are consequences of +how type lists work. + +#### Both type lists and methods in constraints + +A constraint may use both type lists and methods. + +```Go +// StringableSignedInteger is a type constraint that matches any +// type that is both 1) defined as a signed integer type; +// 2) has a String method. +type StringableSignedInteger interface { + type int, int8, int16, int32, int64 + String() string +} +``` + +This constraint permits any type whose underlying type is one of the +listed types, provided it also has a `String() string` method. +It's worth noting that although the `StringableSignedInteger` +constraint explicitly lists `int`, the type `int` will not itself be +permitted as a type argument, since `int` does not have a `String` +method. +An example of a type argument that would be permitted is `MyInt`, +defined as: + +```Go +// MyInt is a stringable int. +type MyInt int + +// The String method returns a string representation of mi. +func (mi MyInt) String() string { + return fmt.Sprintf("MyInt(%d)", mi) +} +``` + +#### Composite types in constraints + +A type in a constraint may be a type literal. + +```Go +type byteseq interface { + type string, []byte +} +``` + +The usual rules apply: the type argument for this constraint may be +`string` or `[]byte` or a type defined as one of those types; a +generic function with this constraint may use any operation permitted +by both `string` and `[]byte`. + +The `byteseq` constraint permits writing generic functions that work +for either `string` or `[]byte` types. + +```Go +// Join concatenates the elements of its first argument to create a +// single value. sep is placed between elements in the result. +// Join works for string and []byte types. +func Join(type T byteseq)(a []T, sep T) (ret T) { + if len(a) == 0 { + // Use the result parameter as a zero value; + // see discussion of zero value in the Issues section. + return ret + } + if len(a) == 1 { + // We know that a[0] is either a string or a []byte. + // We can append either a string or a []byte to a []byte, + // producing a []byte. We can convert that []byte to + // either a []byte (a no-op conversion) or a string. + return T(append([]byte(nil), a[0]...)) + } + // We can call len on sep because we can call len + // on both string and []byte. + n := len(sep) * (len(a) - 1) + for _, v := range a { + // Another case where we call len on string or []byte. + n += len(v) + } + + b := make([]byte, n) + // We can call copy to a []byte with an argument of + // either string or []byte. + bp := copy(b, a[0]) + for _, s := range a[1:] { + bp += copy(b[bp:], sep) + bp += copy(b[bp:], s) + } + // As above, we can convert b to either []byte or string. + return T(b) +} +``` + +#### Type parameters in type lists + +A type literal in a constraint can refer to type parameters of the +constraint. +In this example, the generic function `Map` takes two type parameters. +The first type parameter is required to have an underlying type that +is a slice of the second type parameter. +There are no constraints on the second slice parameter. + +```Go +// SliceConstraint is a type constraint that matches a slice of +// the type parameter. +type SliceConstraint(type T) interface { + type []T +} + +// Map takes a slice of some element type and a transformation function, +// and returns a slice of the function applied to each element. +// Map returns a slice that is the same type as its slice argument, +// even if that is a defined type. +func Map(type S SliceConstraint(E), E interface{})(s S, f func(E) E) S { + r := make(S, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} + +// MySlice is a simple defined type. +type MySlice []int + +// DoubleMySlice takes a value of type MySlice and returns a new +// MySlice value with each element doubled in value. +func DoubleMySlice(s MySlice) MySlice { + v := Map(MySlice, int)(s, func(e int) int { return 2 * e }) + // Here v has type MySlice, not type []int. + return v +} +``` + +#### Type conversions + +In a function with two type parameters `From` and `To`, a value of +type `From` may be converted to a value of type `To` if all the +types accepted by `From`'s constraint can be converted to all the +types accepted by `To`'s constraint. +If either type parameter does not accept types, then type conversions +are not permitted. + +This is a consequence of the general rule that a generic function may +use any operation that is permitted by all types listed in the type +list. + +For example: + +```Go +type integer interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr +} + +func Convert(type To, From integer)(from From) To { + to := To(from) + if From(to) != from { + panic("conversion out of range") + } + return to +} +``` + +The type conversions in `Convert` are permitted because Go permits +every integer type to be converted to every other integer type. + +#### Untyped constants + +Some functions use untyped constants. +An untyped constant is permitted with a value of some type parameter +if it is permitted with every type accepted by the type parameter's +constraint. + +As with type conversions, this is a consequence of the general rule +that a generic function may use any operation that is permitted by all +types listed in the type list. + +```Go +type integer interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr +} + +func Add10(type T integer)(s []T) { + for i, v := range s { + s[i] = v + 10 // OK: 10 can convert to any integer type + } +} + +// This function is INVALID. +func Add1024(type T integer)(s []T) { + for i, v := range s { + s[i] = v + 1024 // INVALID: 1024 not permitted by int8/uint8 + } +} +``` + +#### Notes on composite types in type lists + +It's not clear that we fully understand the use of composite types in +type lists. +For example, consider + +```Go +type structField interface { + type struct { a int; x int }, + struct { b int; x float64 }, + struct { c int; x uint64 } +} + +func IncrementX(type T structField)(p *T) { + v := p.x + v++ + p.x = v +} +``` + +This constraint on the type parameter of `IncrementX` is such that +every valid type argument is a struct with a field `x` of some numeric +type. +Therefore, it is tempting to say that `IncrementX` is a valid +function. +This would mean that the type of `v` is a type based on a type +parameter, with an implicit constraint of `interface { type int, +float64, uint64 }`. +This could get fairly complex, and there may be details here that we +don't understand. + +The initial implementation may not support composite types in type +lists at all, although that would make the `Join` example shown +earlier invalid. + +#### Type lists in embedded constraints + +When a constraint embeds another constraint, the type list of the +final constraint is the intersection of all the type lists involved. +If there are multiple embedded types, intersection preserves the +property that any type argument must satisfy the requirements of all +embedded types. + +```Go +// Addable is types that support the + operator. +type Addable interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64, complex64, complex128, + string +} + +// Byteseq is a byte sequence: either string or []byte. +type Byteseq interface { + type string, []byte +} + +// AddableByteseq is a byte sequence that supports +. +// This is every type is that is both Addable and Byteseq. +// In other words, just the type string. +type AddableByteseq interface { + Addable + Byteseq +} +``` + +#### General notes on type lists + +It may seem awkward to explicitly list types in a constraint, but it +is clear both as to which type arguments are permitted at the call +site, and which operations are permitted by the generic function. + +If the language later changes to support operator methods (there are +no such plans at present), then constraints will handle them as they +do any other kind of method. + +There will always be a limited number of predeclared types, and a +limited number of operators that those types support. +Future language changes will not fundamentally change those facts, so +this approach will continue to be useful. + +This approach does not attempt to handle every possible operator. +It's not clear that it works well for composite types. +The expectation is that those will be handled using composite types in +generic function and type declarations, rather than requiring +composite types as a type argument. +For example, we expect functions that want to index into a slice to be +parameterized on the slice element type `T`, and to use parameters or +variables of type `[]T`. + +As shown in the `DoubleMySlice` example above, this approach makes it +awkward to declare generic functions that accept and return a +composite type and want to return the same result type as their +argument type. +Defined composite types are not common, but they do arise. +This awkwardness is a weakness of this approach. + +### Reflection + +We do not propose to change the reflect package in any way. +When a type or function is instantiated, all of the type parameters +will become ordinary non-generic types. +The `String` method of a `reflect.Type` value of an instantiated type +will return the name with the type arguments in parentheses. +For example, `List(int)`. + +It's impossible for non-generic code to refer to generic code without +instantiating it, so there is no reflection information for +uninstantiated generic types or functions. + +### Implementation + +Russ Cox [famously observed](https://research.swtch.com/generic) that +generics require choosing among slow programmers, slow compilers, or +slow execution times. + +We believe that this design permits different implementation choices. +Code may be compiled separately for each set of type arguments, or it +may be compiled as though each type argument is handled similarly to +an interface type with method calls, or there may be some combination +of the two. + +In other words, this design permits people to stop choosing slow +programmers, and permits the implementation to decide between slow +compilers (compile each set of type arguments separately) or slow +execution times (use method calls for each operation on a value of a +type argument). + +### Summary + +While this document is long and detailed, the actual design reduces to +a few major points. + +* Functions and types can have type parameters, which are defined + using optional constraints, which are interface types. +* Constraints describe the methods required and the types permitted + for a type argument. +* Constraints describe the methods and operations permitted for a type + parameter. +* Type inference will often permit omitting type arguments when + calling functions with type parameters. + +This design is completely backward compatible, except for a suggested +change in the meaning of `func F(x(T))`. + +We believe that this design addresses people's needs for generic +programming in Go, without making the language any more complex than +necessary. + +We can't truly know the impact on the language without years of +experience with this design. +That said, here are some speculations. + +#### Complexity + +One of the great aspects of Go is its simplicity. +Clearly this design makes the language more complex. + +We believe that the increased complexity is small for people reading +well written generic code, rather than writing it. +Naturally people must learn the new syntax for declaring type +parameters. +This new syntax, and the new support for type lists in interfaces, are +the only new syntactic constructs in this design. +The code within a generic function reads like ordinary Go code, as can +be seen in the examples below. +It is an easy shift to go from `[]int` to `[]T`. +Type parameter constraints serve effectively as documentation, +describing the type. + +We expect that most packages will not define generic types or +functions, but many packages are likely to use generic types or +functions defined elsewhere. +In the common case, generic functions work exactly like non-generic +functions: you simply call them. +Type inference means that you do not have to write out the type +arguments explicitly. +The type inference rules are designed to be unsurprising: either the +type arguments are deduced correctly, or the call fails and requires +explicit type parameters. +Type inference uses type identity, with no attempt to resolve two +types that are similar but not identical, which removes significant +complexity. + +Packages using generic types will have to pass explicit type +arguments. +The syntax for this is familiar. +The only change is passing arguments to types rather than only to +functions. + +In general, we have tried to avoid surprises in the design. +Only time will tell whether we succeeded. + +#### Pervasiveness + +We expect that a few new packages will be added to the standard +library. +A new `slices` packages will be similar to the existing bytes and +strings packages, operating on slices of any element type. +New `maps` and `chans` packages will provide simple algorithms that +are currently duplicated for each element type. +A `set` package may be added. + +A new `constraints` package will provide standard constraints, such as +constraints that permit all integer types or all numeric types. + +Packages like `container/list` and `container/ring`, and types like +`sync.Map` and `sync/atomic.Value`, will be updated to be compile-time +type-safe, either using new names or new versions of the packages. + +The `math` package will be extended to provide a set of simple +standard algorithms for all numeric types, such as the ever popular +`Min` and `Max` functions. + +We may add generic variants to the `sort` package. + +It is likely that new special purpose compile-time type-safe container +types will be developed. + +We do not expect approaches like the C++ STL iterator types to become +widely used. +In Go that sort of idea is more naturally expressed using an interface +type. +In C++ terms, using an interface type for an iterator can be seen as +carrying an abstraction penalty, in that run-time efficiency will be +less than C++ approaches that in effect inline all code; we believe +that Go programmers will continue to find that sort of penalty to be +acceptable. + +As we get more container types, we may develop a standard `Iterator` +interface. +That may in turn lead to pressure to modify the language to add some +mechanism for using an `Iterator` with the `range` clause. +That is very speculative, though. + +#### Efficiency + +It is not clear what sort of efficiency people expect from generic +code. + +Generic functions, rather than generic types, can probably be compiled +using an interface-based approach. +That will optimize compile time, in that the function is only compiled +once, but there will be some run time cost. + +Generic types may most naturally be compiled multiple times for each +set of type arguments. +This will clearly carry a compile time cost, but there shouldn't be +any run time cost. +Compilers can also choose to implement generic types similarly to +interface types, using special purpose methods to access each element +that depends on a type parameter. + +Only experience will show what people expect in this area. + +#### Omissions + +We believe that this design covers the basic requirements for +generic programming. +However, there are a number of programming constructs that are not +supported. + +* No specialization. + There is no way to write multiple versions of a generic function + that are designed to work with specific type arguments. +* No metaprogramming. + There is no way to write code that is executed at compile time to + generate code to be executed at run time. +* No higher level abstraction. + There is no way to speak about a function with type arguments other + than to call it or instantiate it. + There is no way to speak about a generic type other than to + instantiate it. +* No general type description. + In order to use operators in a generic function, constraints list + specific types, rather than describing the characteristics that a + type must have. + This is easy to understand but may be limiting at times. +* No covariance or contravariance of function parameters. +* No operator methods. + You can write a generic container that is compile-time type-safe, + but you can only access it with ordinary methods, not with syntax + like `c[k]`. +* No currying. + There is no way to specify only some of the type arguments, other + than by using a helper function or a wrapper type. +* No variadic type parameters. + There is no support for variadic type parameters, which would permit + writing a single generic function that takes different numbers of + both type parameters and regular parameters. +* No adaptors. + There is no way for a constraint to define adaptors that could be + used to support type arguments that do not already implement the + constraint, such as, for example, defining an `==` operator in terms + of an `Equal` method, or vice-versa. +* No parameterization on non-type values such as constants. + This arises most obviously for arrays, where it might sometimes be + convenient to write `type Matrix(type n int) [n][n]float64`. + It might also sometimes be useful to specify significant values for + a container type, such as a default value for elements. + +#### Issues + +There are some issues with this design that deserve a more detailed +discussion. +We think these issues are relatively minor compared to the design as a +whole, but they still deserve a complete hearing and discussion. + +##### The zero value + +This design has no simple expression for the zero value of a type +parameter. +For example, consider this implementation of optional values that uses +pointers: + +```Go +type Optional(type T) struct { + p *T +} + +func (o Optional(T)) Val() T { + if o.p != nil { + return *o.p + } + var zero T + return zero +} +``` + +In the case where `o.p == nil`, we want to return the zero value of +`T`, but we have no way to write that. +It would be nice to be able to write `return nil`, but that wouldn't +work if `T` is, say, `int`; in that case we would have to write +`return 0`. +And, of course, there is no way to write a constraint to support +either `return nil` or `return 0`. + +Some approaches to this are: + +* Use `var zero T`, as above, which works with the existing design + but requires an extra statement. +* Use `*new(T)`, which is cryptic but works with the existing + design. +* For results only, name the result parameter `_`, and use a naked + `return` statement to return the zero value. +* Extend the design to permit using `nil` as the zero value of any + generic type (but see [issue 22729](https://golang.org/issue/22729)). +* Extend the design to permit using `T{}`, where `T` is a type + parameter, to indicate the zero value of the type. +* Change the language to permit using `_` on the right hand of an + assignment (including `return` or a function call) as proposed in + [issue 19642](https://golang.org/issue/19642). +* Change the language to permit `return ...` to return zero values of + the result types, as proposed in + [issue 21182](https://golang.org/issue/21182). + +We feel that more experience with this design is needed before +deciding what, if anything, to do here. + +##### Lots of Irritating Silly Parentheses + +Calling a function with type parameters requires an additional list of +type arguments if the type arguments can not be inferred. +If the function returns a function, and we call that, we get still +more parentheses. + +```Go + F(int, float64)(x, y)(s) +``` + +We experimented with other syntaxes, such as using a colon to separate +the type arguments from the regular arguments. +The current design seems to us to be the nicest, but perhaps something +better is possible. + +##### Defined composite types + +As [discussed above](#Type parameters in type lists), an extra type +parameter is required for a function to take, as an argument, a +defined type whose underlying type is a composite type, and to return +the same defined type as a result. + +For example, this function will map a function across a slice. + +```Go +// Map applies f to each element of s, returning a new slice +// holding the results. +func Map(type T)(s []T, f func(T) T) []T { + r := make([]T, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} +``` + +However, when called on a defined type, it will return a slice of the +element type of that type, rather than the defined type itself. + +```Go +// MySlice is a defined type. +type MySlice []int + +// DoubleMySlice returns a new MySlice whose elements are twice +// that of the corresponding elements of s. +func DoubleMySlice(s MySlice) MySlice { + s2 := Map(s, func(e int) int { return 2 * e }) + // Here s2 is type []int, not type MySlice. + return MySlice(s2) +} +``` + +As [discussed above](#Type parameters in type lists), this can be +avoided by using an extra type parameter for `Map`, and using +constraints that describe the required relationship between the slice +and element types. +This works but is awkward. + +##### Identifying the matched predeclared type + +The design doesn't provide any way to test the underlying type matched +by a type argument. +Code can test the actual type argument through the somewhat awkward +approach of converting to an empty interface type and using a type +assertion or a type switch. +But that lets code test the actual type argument, which is not the +same as the underlying type. + +Here is an example that shows the difference. + +```Go +type Float interface { + type float32, float64 +} + +func NewtonSqrt(type T Float)(v T) T { + var iterations int + switch (interface{})(v).(type) { + case float32: + iterations = 4 + case float64: + iterations = 5 + default: + panic(fmt.Sprintf("unexpected type %T", v)) + } + // Code omitted. +} + +type MyFloat float32 + +var G = NewtonSqrt(MyFloat(64)) +``` + +This code will panic when initializing `G`, because the type of `v` in +the `NewtonSqrt` function will be `MyFloat`, not `float32` or +`float64`. +What this function actually wants to test is not the type of `v`, but +the type that `v` matched in the constraint. + +One way to handle this would be to permit type switches on the type +`T`, with the proviso that the type `T` would always match a type +defined in the constraint. +This kind of type switch would only be permitted if the constraint +lists explicit types, and only types listed in the constraint would be +permitted as cases. + +##### No way to express convertability + +The design has no way to express convertability between two different +type parameters. +For example, there is no way to write this function: + +```Go +// Copy copies values from src to dst, converting them as they go. +// It returns the number of items copied, which is the minimum of +// the lengths of dst and src. +// This implementation is INVALID. +func Copy(type T1, T2)(dst []T1, src []T2) int { + for i, x := range src { + if i > len(dst) { + return i + } + dst[i] = T1(x) // INVALID + } + return len(src) +} +``` + +The conversion from type `T2` to type `T1` is invalid, as there is no +constraint on either type that permits the conversion. +Worse, there is no way to write such a constraint in general. +In the particular case that both `T1` and `T2` can require some type +list, then this function can be written as described earlier when +discussing [type conversions using type lists](#Type conversions). +But, for example, there is no way to write a constraint for the case +in which `T1` is an interface type and `T2` is a type that implements +that interface. + +It's worth noting that if `T1` is an interface type then this can be +written using a conversion to the empty interface type and a type +assertion, but this is, of course, not compile-time type-safe. + +```Go +// Copy copies values from src to dst, converting them as they go. +// It returns the number of items copied, which is the minimum of +// the lengths of dst and src. +func Copy(type T1, T2)(dst []T1, src []T2) int { + for i, x := range src { + if i > len(dst) { + return i + } + dst[i] = (interface{})(x).(T1) + } + return len(src) +} +``` + +#### Discarded ideas + +This design is not perfect, and it will be further refined as we gain +experience with it. +That said, there are many ideas that we've already considered in +detail. +This section lists some of those ideas in the hopes that it will help +to reduce repetitive discussion. +The ideas are presented in the form of a FAQ. + +##### What happened to contracts? + +An earlier draft design of generics implemented constraints using a +new language construct called contracts. +Type lists appeared only in contracts, rather than on interface +types. +However, many people had a hard time understanding the difference +between contracts and interface types. +It also turned out that contracts could be represented as a set of +corresponding interfaces; thus there was no loss in expressive power +without contracts. +We decided to simplify the approach to use only interface types. + +##### Why not use methods instead of type lists? + +_Type lists are weird._ +_Why not write methods for all operators?_ + +It is possible to permit operator tokens as method names, leading to +methods such as `+(T) T`. +Unfortunately, that is not sufficient. +We would need some mechanism to describe a type that matches any +integer type, for operations such as shifts `<<(integer) T` and +indexing `[](integer) T` which are not restricted to a single int +type. +We would need an untyped boolean type for operations such as `==(T) +untyped bool`. +We would need to introduce new notation for operations such as +conversions, or to express that one may range over a type, which would +likely require some new syntax. +We would need some mechanism to describe valid values of untyped +constants. +We would have to consider whether support for `<(T) bool` means that a +generic function can also use `<=`, and similarly whether support for +`+(T) T` means that a function can also use `++`. +It might be possible to make this approach work but it's not +straightforward. +The approach used in this design seems simpler and relies on only one +new syntactic construct (type lists) and one new name (`comparable`). + +##### Why not put type parameters on packages? + +We investigated this extensively. +It becomes problematic when you want to write a `list` package, and +you want that package to include a `Transform` function that converts +a `List` of one element type to a `List` of another element type. +It's very awkward for a function in one instantiation of a package to +return a type that requires a different instantiation of the same +package. + +It also confuses package boundaries with type definitions. +There is no particular reason to think that the uses of generic types +will break down neatly into packages. +Sometimes they will, sometimes they won't. + +##### Why not use the syntax `F<T>` like C++ and Java? + +When parsing code within a function, such as `v := F<T>`, at the point +of seeing the `<` it's ambiguous whether we are seeing a type +instantiation or an expression using the `<` operator. +Resolving that requires effectively unbounded lookahead. +In general we strive to keep the Go parser efficient. + +##### Why not use the syntax `F[T]`? + +When parsing a type declaration `type A [T] int` it's ambiguous +whether this is a generic type defined (uselessly) as `int` or whether +it is an array type with `T` elements. +However, this could be addressed by requiring `type A [type T] int` +for a generic type. + +Parsing declarations like `func f(A[T]int)` (a single parameter of +type `[T]int`) and `func f(A[T], int)` (two parameters, one of type +`A[T]` and one of type `int`) show that some additional parsing +lookahead is required. +This is solvable but adds parsing complexity. + +The language generally permits a trailing comma in a comma-separated +list, so `A[T,]` should be permitted if `A` is a generic type, but +normally would not be permitted for an index expression. +However, the parser can't know whether `A` is a generic type or a +value of slice, array, or map type, so this parse error can not be +reported until after type checking is complete. +Again, solvable but complicated. + +More generally, we felt that the square brackets were too intrusive on +the page and that parentheses were more Go like. +We will reevaluate this decision as we gain more experience. + +##### Why not use `F«T»`? + +We considered it but we couldn't bring ourselves to require +non-ASCII. + +##### Why not define constraints in a builtin package? + +_Instead of writing out type lists, use names like_ +_`constraints.Arithmetic` and `constraints.Comparable`._ + +Listing all the possible combinations of types gets rather lengthy. +It also introduces a new set of names that not only the writer of +generic code, but, more importantly, the reader, must remember. +One of the driving goals of this design is to introduce as few new +names as possible. +In this design we introduce only one new predeclared name. + +We expect that if people find such names useful, we can introduce a +package `constraints` that defines the useful names in the form of +constraints that can be used by other types and functions and embedded +in other constraints. +That will define the most useful names in the standard library while +giving programmers the flexibility to use other combinations of types +where appropriate. + +##### Why not permit type assertions on values whose type is a type parameter? + +In an earlier version of this design, we permitted using type +assertions and type switches on variables whose type was a type +parameter, or whose type was based on a type parameter. +We removed this facility because it is always possible to convert a +value of any type to the empty interface type, and then use a type +assertion or type switch on that. +Also, it was sometimes confusing that in a constraint with a type +list, a type assertion or type switch would use the actual type +argument, not the underlying type of the type argument (the difference +is explained in the section on [identifying the matched predeclared +type](#Identifying the matched predeclared type)). + +#### Comparison with Java + +Most complaints about Java generics center around type erasure. +This design does not have type erasure. +The reflection information for a generic type will include the full +compile-time type information. + +In Java type wildcards (`List<? extends Number>`, `List<? super +Number>`) implement covariance and contravariance. +These concepts are missing from Go, which makes generic types much +simpler. + +#### Comparison with C++ + +C++ templates do not enforce any constraints on the type arguments +(unless the concept proposal is adopted). +This means that changing template code can accidentally break far-off +instantiations. +It also means that error messages are reported only at instantiation +time, and can be deeply nested and difficult to understand. +This design avoids these problems through explicit required +constraints. + +C++ supports template metaprogramming, which can be thought of as +ordinary programming done at compile time using a syntax that is +completely different than that of non-template C++. +This design has no similar feature. +This saves considerable complexity while losing some power and run +time efficiency. + +C++ uses two-phase name lookup, in which some names are looked up in +the context of the template definition, and some names are looked up +in the context of the template instantiation. +In this design all names are looked up at the point where they are +written. + +In practice, all C++ compilers compile each template at the point +where it is instantiated. +This can slow down compilation time. +This design offers flexibility as to how to handle the compilation of +generic functions. + +#### Comparison with Rust + +The generics described in this design are similar to generics in +Rust. + +One difference is that in Rust the association between a trait bound +and a type must be defined explicitly, either in the crate that +defines the trait bound or the crate that defines the type. +In Go terms, this would mean that we would have to declare somewhere +whether a type satisfied a constraint. +Just as Go types can satisfy Go interfaces without an explicit +declaration, in this design Go type arguments can satisfy a constraint +without an explicit declaration. + +Where this design uses type lists, the Rust standard library defines +standard traits for operations like comparison. +These standard traits are automatically implemented by Rust's +primitive types, and can be implemented by user defined types as +well. +Rust provides a fairly extensive list of traits, at least 34, covering +all of the operators. + +Rust supports type parameters on methods, which this design does not. + +## Examples + +The following sections are examples of how this design could be used. +This is intended to address specific areas where people have created +user experience reports concerned with Go's lack of generics. + +### Map/Reduce/Filter + +Here is an example of how to write map, reduce, and filter functions +for slices. +These functions are intended to correspond to the similar functions in +Lisp, Python, Java, and so forth. + +```Go +// Package slices implements various slice algorithms. +package slices + +// Map turns a []T1 to a []T2 using a mapping function. +// This function has two type parameters, T1 and T2. +// There are no constraints on the type parameters, +// so this works with slices of any type. +func Map(type T1, T2)(s []T1, f func(T1) T2) []T2 { + r := make([]T2, len(s)) + for i, v := range s { + r[i] = f(v) + } + return r +} + +// Reduce reduces a []T1 to a single value using a reduction function. +func Reduce(type T1, T2)(s []T1, initializer T2, f func(T2, T1) T2) T2 { + r := initializer + for _, v := range s { + r = f(r, v) + } + return r +} + +// Filter filters values from a slice using a filter function. +// It returns a new slice with only the elements of s +// for which f returned true. +func Filter(type T)(s []T, f func(T) bool) []T { + var r []T + for _, v := range s { + if f(v) { + r = append(r, v) + } + } + return r +} +``` + +Here are some example calls of these functions. +Type inference is used to determine the type arguments based on the +types of the non-type arguments. + +```Go + s := []int{1, 2, 3} + + floats := slices.Map(s, func(i int) float64 { return float64(i) }) + // Now float2 is []float64{1.0, 2.0, 3.0}. + + sum := slices.Reduce(s, 0, func(i, j int) int { return i + j }) + // Now sum is 6. + + evens := slices.Filter(s, func(i int) bool { return i%2 == 0 }) + // Now evens is []int{2}. +``` + +### Map keys + +Here is how to get a slice of the keys of any map. + +```Go +// Package maps provides general functions that work for all map types. +package maps + +// Keys returns the keys of the map m in a slice. +// The keys will be returned in an unpredictable order. +// This function has two type parameters, K and V. +// Map keys must be comparable, so key has the predeclared +// constraint comparable. Map values can be any type; +// the empty interface type imposes no constraints. +func Keys(type K comparable, V interface{})(m map[K]V) []K { + r := make([]K, 0, len(m)) + for k := range m { + r = append(r, k) + } + return r +} +``` + +In typical use the map key and val types will be inferred. + +```Go + k := maps.Keys(map[int]int{1:2, 2:4}) + // Now k is either []int{1, 2} or []int{2, 1}. +``` + +### Sets + +Many people have asked for Go's builtin map type to be extended, or +rather reduced, to support a set type. +Here is a type-safe implementation of a set type, albeit one that uses +methods rather than operators like `[]`. + +```Go +// Package set implements sets of any comparable type. +package set + +// Set is a set of values. +type Set(type T comparable) map[T]struct{} + +// Make returns a set of some element type. +func Make(type T comparable)() Set(T) { + return make(Set(T)) +} + +// Add adds v to the set s. +// If v is already in s this has no effect. +func (s Set(T)) Add(v T) { + s[v] = struct{}{} +} + +// Delete removes v from the set s. +// If v is not in s this has no effect. +func (s Set(T)) Delete(v T) { + delete(s, v) +} + +// Contains reports whether v is in s. +func (s Set(T)) Contains(v T) bool { + _, ok := s[v] + return ok +} + +// Len reports the number of elements in s. +func (s Set(T)) Len() int { + return len(s) +} + +// Iterate invokes f on each element of s. +// It's OK for f to call the Delete method. +func (s Set(T)) Iterate(f func(T)) { + for v := range s { + f(v) + } +} +``` + +Example use: + +```Go + // Create a set of ints. + // We pass (int) as a type argument. + // Then we write () because Make does not take any non-type arguments. + // We have to pass an explicit type argument to Make. + // Type inference doesn't work because the type argument + // to Make is only used for a result parameter type. + s := set.Make(int)() + + // Add the value 1 to the set s. + s.Add(1) + + // Check that s does not contain the value 2. + if s.Contains(2) { panic("unexpected 2") } +``` + +This example shows how to use this design to provide a compile-time +type-safe wrapper around an existing API. + +### Sort + +Before the introduction of `sort.Slice`, a common complaint was the +need for boilerplate definitions in order to use `sort.Sort`. +With this design, we can add to the sort package as follows: + +```Go +// Ordered is a type constraint that matches all ordered types. +// (An ordered type is one that supports the < <= >= > operators.) +// In practice this type constraint would likely be defined in +// a standard library package. +type Ordered interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64, + string +} + +// orderedSlice is an internal type that implements sort.Interface. +// The Less method uses the < operator. The Ordered type constraint +// ensures that T has a < operator. +type orderedSlice(type T Ordered) []T + +func (s orderedSlice(T)) Len() int { return len(s) } +func (s orderedSlice(T)) Less(i, j int) bool { return s[i] < s[j] } +func (s orderedSlice(T)) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// OrderedSlice sorts the slice s in ascending order. +// The elements of s must be ordered using the < operator. +func OrderedSlice(type T Ordered)(s []T) { + // Convert s to the type orderedSlice(T). + // As s is []T, and orderedSlice(T) is defined as []T, + // this conversion is permitted. + // orderedSlice(T) implements sort.Interface, + // so can pass the result to sort.Sort. + // The elements will be sorted using the < operator. + sort.Sort(orderedSlice(T)(s)) +} +``` + +Now we can write: + +```Go + s1 := []int32{3, 5, 2} + sort.OrderedSlice(s1) + // Now s1 is []int32{2, 3, 5} + + s2 := []string{"a", "c", "b"}) + sort.OrderedSlice(s2) + // Now s2 is []string{"a", "b", "c"} +``` + +Along the same lines, we can add a function for sorting using a +comparison function, similar to `sort.Slice` but writing the function +to take values rather than slice indexes. + +```Go +// sliceFn is an internal type that implements sort.Interface. +// The Less method calls the cmp field. +type sliceFn(type T) struct { + s []T + cmp func(T, T) bool +} + +func (s sliceFn(T)) Len() int { return len(s.s) } +func (s sliceFn(T)) Less(i, j int) bool { return s.cmp(s.s[i], s.s[j]) } +func (s sliceFn(T)) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] } + +// SliceFn sorts the slice s according to the function cmp. +func SliceFn(type T)(s []T, cmp func(T, T) bool) { + Sort(sliceFn(E){s, cmp}) +} +``` + +An example of calling this might be: + +```Go + var s []*Person + // ... + sort.SliceFn(s, func(p1, p2 *Person) bool { return p1.Name < p2.Name }) +``` + +### Channels + +Many simple general purpose channel functions are never written, +because they must be written using reflection and the caller must type +assert the results. +With this design they become straightforward to write. + +```Go +// Package chans implements various channel algorithms. +package chans + +import "runtime" + +// Ranger provides a convenient way to exit a goroutine sending values +// when the receiver stops reading them. +// +// Ranger returns a Sender and a Receiver. The Receiver provides a +// Next method to retrieve values. The Sender provides a Send method +// to send values and a Close method to stop sending values. The Next +// method indicates when the Sender has been closed, and the Send +// method indicates when the Receiver has been freed. +func Ranger(type T)() (*Sender(T), *Receiver(T)) { + c := make(chan T) + d := make(chan bool) + s := &Sender(T){values: c, done: d} + r := &Receiver(T){values: c, done: d} + // The finalizer on the receiver will tell the sender + // if the receiver stops listening. + runtime.SetFinalizer(r, r.finalize) + return s, r +} + +// A Sender is used to send values to a Receiver. +type Sender(type T) struct { + values chan<- T + done <-chan bool +} + +// Send sends a value to the receiver. It reports whether any more +// values may be sent; if it returns false the value was not sent. +func (s *Sender(T)) Send(v T) bool { + select { + case s.values <- v: + return true + case <-s.done: + // The receiver has stopped listening. + return false + } +} + +// Close tells the receiver that no more values will arrive. +// After Close is called, the Sender may no longer be used. +func (s *Sender(T)) Close() { + close(s.values) +} + +// A Receiver receives values from a Sender. +type Receiver(type T) struct { + values <-chan T + done chan<- bool +} + +// Next returns the next value from the channel. The bool result +// reports whether the value is valid. If the value is not valid, the +// Sender has been closed and no more values will be received. +func (r *Receiver(T)) Next() (T, bool) { + v, ok := <-r.values + return v, ok +} + +// finalize is a finalizer for the receiver. +// It tells the sender that the receiver has stopped listening. +func (r *Receiver(T)) finalize() { + close(r.done) +} +``` + +There is an example of using this function in the next section. + +### Containers + +One of the frequent requests for generics in Go is the ability to +write compile-time type-safe containers. +This design makes it easy to write a compile-time type-safe wrapper +around an existing container; we won't write out an example for that. +This design also makes it easy to write a compile-time type-safe +container that does not use boxing. + +Here is an example of an ordered map implemented as a binary tree. +The details of how it works are not too important. +The important points are: + +* The code is written in a natural Go style, using the key and value + types where needed. +* The keys and values are stored directly in the nodes of the tree, + not using pointers and not boxed as interface values. + +```Go +// Package orderedmap provides an ordered map, implemented as a binary tree. +package orderedmap + +import "chans" + +// Map is an ordered map. +type Map(type K, V) struct { + root *node(K, V) + compare func(K, K) int +} + +// node is the type of a node in the binary tree. +type node(type K, V) struct { + k K + v V + left, right *node(K, V) +} + +// New returns a new map. +// Since the type parameter V is only used for the result, +// type inference does not work, and calls to New must always +// pass explicit type arguments. +func New(type K, V)(compare func(K, K) int) *Map(K, V) { + return &Map(K, V){compare: compare} +} + +// find looks up k in the map, and returns either a pointer +// to the node holding k, or a pointer to the location where +// such a node would go. +func (m *Map(K, V)) find(k K) **node(K, V) { + pn := &m.root + for *pn != nil { + switch cmp := m.compare(k, (*pn).k); { + case cmp < 0: + pn = &(*pn).left + case cmp > 0: + pn = &(*pn).right + default: + return pn + } + } + return pn +} + +// Insert inserts a new key/value into the map. +// If the key is already present, the value is replaced. +// Reports whether this is a new key. +func (m *Map(K, V)) Insert(k K, v V) bool { + pn := m.find(k) + if *pn != nil { + (*pn).v = v + return false + } + *pn = &node(K, V){k: k, v: v} + return true +} + +// Find returns the value associated with a key, or zero if not present. +// The bool result reports whether the key was found. +func (m *Map(K, V)) Find(k K) (V, bool) { + pn := m.find(k) + if *pn == nil { + var zero val // see the discussion of zero values, above + return zero, false + } + return (*pn).v, true +} + +// keyValue is a pair of key and value used when iterating. +type keyValue(type K, V) struct { + k K + v V +} + +// InOrder returns an iterator that does an in-order traversal of the map. +func (m *Map(K, V)) InOrder() *Iterator(K, V) { + type kv = keyValue(K, V) // convenient shorthand + sender, receiver := chans.Ranger(kv)() + var f func(*node(K, V)) bool + f = func(n *node(K, V)) bool { + if n == nil { + return true + } + // Stop sending values if sender.Send returns false, + // meaning that nothing is listening at the receiver end. + return f(n.left) && + sender.Send(kv{n.k, n.v}) && + f(n.right) + } + go func() { + f(m.root) + sender.Close() + }() + return &Iterator{receiver} +} + +// Iterator is used to iterate over the map. +type Iterator(type K, V) struct { + r *chans.Receiver(keyValue(K, V)) +} + +// Next returns the next key and value pair. The bool result reports +// whether the values are valid. If the values are not valid, we have +// reached the end. +func (it *Iterator(K, V)) Next() (K, V, bool) { + kv, ok := it.r.Next() + return kv.k, kv.v, ok +} +``` + +This is what it looks like to use this package: + +```Go +import "container/orderedmap" + +// Set m to an ordered map from string to string, +// using strings.Compare as the comparison function. +var m = orderedmap.New(string, string)(strings.Compare) + +// Add adds the pair a, b to m. +func Add(a, b string) { + m.Insert(a, b) +} +``` + +### Append + +The predeclared `append` function exists to replace the boilerplate +otherwise required to grow a slice. +Before `append` was added to the language, there was a function `Add` +in the bytes package: + +```Go +// Add appends the contents of t to the end of s and returns the result. +// If s has enough capacity, it is extended in place; otherwise a +// new array is allocated and returned. +func Add(s, t []byte) []byte +``` + +`Add` appended two `[]byte` values together, returning a new slice. +That was fine for `[]byte`, but if you had a slice of some other +type, you had to write essentially the same code to append more +values. +If this design were available back then, perhaps we would not have +added `append` to the language. +Instead, we could write something like this: + +```Go +// Package slices implements various slice algorithms. +package slices + +// Append appends the contents of t to the end of s and returns the result. +// If s has enough capacity, it is extended in place; otherwise a +// new array is allocated and returned. +func Append(type T)(s []T, t ...T) []T { + lens := len(s) + tot := lens + len(t) + if tot < 0 { + panic("Append: cap out of range") + } + if tot > cap(s) { + news := make([]T, tot, tot + tot/2) + copy(news, s) + s = news + } + s = s[:tot] + copy(s[lens:], t) + return s +} +``` + +That example uses the predeclared `copy` function, but that's OK, we +can write that one too: + +```Go +// Copy copies values from t to s, stopping when either slice is +// full, returning the number of values copied. +func Copy(type T)(s, t []T) int { + i := 0 + for ; i < len(s) && i < len(t); i++ { + s[i] = t[i] + } + return i +} +``` + +These functions can be used as one would expect: + +```Go + s := slices.Append([]int{1, 2, 3}, 4, 5, 6) + // Now s is []int{1, 2, 3, 4, 5, 6}. + slices.Copy(s[3:], []int{7, 8, 9}) + // Now s is []int{1, 2, 3, 7, 8, 9} +``` + +This code doesn't implement the special case of appending or copying a +`string` to a `[]byte`, and it's unlikely to be as efficient as the +implementation of the predeclared function. +Still, this example shows that using this design would permit `append` +and `copy` to be written generically, once, without requiring any +additional special language features. + +### Metrics + +In a [Go experience +report](https://medium.com/@sameer_74231/go-experience-report-for-generics-google-metrics-api-b019d597aaa4) +Sameer Ajmani describes a metrics implementation. +Each metric has a value and one or more fields. +The fields have different types. +Defining a metric requires specifying the types of the fields, and +creating a value with an Add method. +The Add method takes the field types as arguments, and records an +instance of that set of fields. +The C++ implementation uses a variadic template. +The Java implementation includes the number of fields in the name of +the type. +Both the C++ and Java implementations provide compile-time type-safe +Add methods. + +Here is how to use this design to provide similar functionality in +Go with a compile-time type-safe Add method. +Because there is no support for a variadic number of type arguments, +we must use different names for a different number of arguments, as in +Java. +This implementation only works for comparable types. +A more complex implementation could accept a comparison function to +work with arbitrary types. + +```Go +// Package metrics provides a general mechanism for accumulating +// metrics of different values. +package metrics + +import "sync" + +// Metric1 accumulates metrics of a single value. +type Metric1(type T comparable) struct { + mu sync.Mutex + m map[T]int +} + +// Add adds an instance of a value. +func (m *Metric1(T)) Add(v T) { + m.mu.Lock() + defer m.mu.Unlock() + if m.m == nil { + m.m = make(map[T]int) + } + m.m[v]++ +} + +// key2 is an internal type used by Metric2. +type key2(type T1, T2 comparable) struct { + f1 T1 + f2 T2 +} + +// Metric2 accumulates metrics of pairs of values. +type Metric2(type T1, T2 comparable) struct { + mu sync.Mutex + m map[key2(T1, T2)]int +} + +// Add adds an instance of a value pair. +func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) { + m.mu.Lock() + defer m.mu.Unlock() + if m.m == nil { + m.m = make(map[key2(T1, T2)]int) + } + m.m[key2(T1, T2){v1, v2}]++ +} + +// key3 is an internal type used by Metric3. +type key3(type T1, T2, T3 comparable) struct { + f1 T1 + f2 T2 + f3 T3 +} + +// Metric3 accumulates metrics of triples of values. +type Metric3(type T1, T2, T3 comparable) struct { + mu sync.Mutex + m map[key3(T1, T2, T3)]int +} + +// Add adds an instance of a value triplet. +func (m *Metric3(T1, T2, T3)) Add(v1 T1, v2 T2, v3 T3) { + m.mu.Lock() + defer m.mu.Unlock() + if m.m == nil { + m.m = make(map[key3(T1, T2, T3)]int) + } + m.m[key3(T1, T2, T3){v1, v2, v3}]++ +} + +// Repeat for the maximum number of permitted arguments. +``` + +Using this package looks like this: + +```Go +import "metrics" + +var m = metrics.Metric2(string, int){} + +func F(s string, i int) { + m.Add(s, i) // this call is type checked at compile time +} +``` + +This implementation has a certain amount of repetition due to the lack +of support for variadic type parameters. +Using the package, though, is easy and type safe. + +### List transform + +While slices are efficient and easy to use, there are occasional cases +where a linked list is appropriate. +This example primarily shows transforming a linked list of one type to +another type, as an example of using different instantiations of the +same generic type. + +```Go +// Package list provides a linked list of any type. +package list + +// List is a linked list. +type List(type T) struct { + head, tail *element(T) +} + +// An element is an entry in a linked list. +type element(type T) struct { + next *element(T) + val T +} + +// Push pushes an element to the end of the list. +func (lst *List(T)) Push(v T) { + if lst.tail == nil { + lst.head = &element(T){val: v} + lst.tail = lst.head + } else { + lst.tail.next = &element(T){val: v } + lst.tail = lst.tail.next + } +} + +// Iterator ranges over a list. +type Iterator(type T) struct { + next **element(T) +} + +// Range returns an Iterator starting at the head of the list. +func (lst *List(T)) Range() *Iterator(T) { + return Iterator(T){next: &lst.head} +} + +// Next advances the iterator. +// It reports whether there are more elements. +func (it *Iterator(T)) Next() bool { + if *it.next == nil { + return false + } + it.next = &(*it.next).next + return true +} + +// Val returns the value of the current element. +// The bool result reports whether the value is valid. +func (it *Iterator(T)) Val() (T, bool) { + if *it.next == nil { + var zero T + return zero, false + } + return (*it.next).val, true +} + +// Transform runs a transform function on a list returning a new list. +func Transform(type T1, T2)(lst *List(T1), f func(T1) T2) *List(T2) { + ret := &List(T2){} + it := lst.Range() + for { + if v, ok := it.Val(); ok { + ret.Push(f(v)) + } + if !it.Next() { + break + } + } + return ret +} +``` + +### Dot product + +A generic dot product implementation that works for slices of any +numeric type. + +```Go +// Numeric is a constraint that matches any numeric type. +// It would likely be in a constraints package in the standard library. +type Numeric interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64, + complex64, complex128 +} + +// DotProduct returns the dot product of two slices. +// This panics if the two slices are not the same length. +func DotProduct(type T Numeric)(s1, s2 []T) T { + if len(s1) != len(s2) { + panic("DotProduct: slices of unequal length") + } + var r T + for i := range s1 { + r += s1[i] * s2[i] + } + return r +} +``` + +(Note: the generics implementation approach may affect whether +`DotProduct` uses FMA, and thus what the exact results are when using +floating point types. +It's not clear how much of a problem this is, or whether there is any +way to fix it.) + +### Absolute difference + +Compute the absolute difference between two numeric values, by using +an `Abs` method. +This uses the same `Numeric` constraint defined in the last example. + +This example uses more machinery than is appropriate for the simple +case of computing the absolute difference. +It is intended to show how the common part of algorithms can be +factored into code that uses methods, where the exact definition of +the methods can vary based on the kind of type being used. + +```Go +// NumericAbs matches numeric types with an Abs method. +type NumericAbs(type T) interface { + Numeric + Abs() T +} + +// AbsDifference computes the absolute value of the difference of +// a and b, where the absolute value is determined by the Abs method. +func AbsDifference(type T NumericAbs)(a, b T) T { + d := a - b + return d.Abs() +} +``` + +We can define an `Abs` method appropriate for different numeric types. + +```Go +// OrderedNumeric matches numeric types that support the < operator. +type OrderedNumeric interface { + type int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64 +} + +// Complex matches the two complex types, which do not have a < operator. +type Complex interface { + type complex64, complex128 +} + +// OrderedAbs is a helper type that defines an Abs method for +// ordered numeric types. +type OrderedAbs(type T OrderedNumeric) T + +func (a OrderedAbs(T)) Abs() OrderedAbs(T) { + if a < 0 { + return -a + } + return a +} + +// ComplexAbs is a helper type that defines an Abs method for +// complex types. +type ComplexAbs(type T Complex) T + +func (a ComplexAbs(T)) Abs() ComplexAbs(T) { + d := math.Hypot(float64(real(a)), float64(imag(a))) + return ComplexAbs(T)(complex(d, 0)) +} +``` + +We can then define functions that do the work for the caller by +converting to and from the types we just defined. + +```Go +// OrderedAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of an ordered type. +func OrderedAbsDifference(type T OrderedNumeric)(a, b T) T { + return T(AbsDifference(OrderedAbs(T)(a), OrderedAbs(T)(b))) +} + +// ComplexAbsDifference returns the absolute value of the difference +// between a and b, where a and b are of a complex type. +func ComplexAbsDifference(type T Complex)(a, b T) T { + return T(AbsDifference(ComplexAbs(T)(a), ComplexAbs(T)(b))) +} +``` + +It's worth noting that this design is not powerful enough to write +code like the following: + +```Go +// This function is INVALID. +func GeneralAbsDifference(type T Numeric)(a, b T) T { + switch (interface{})(a).(type) { + case int, int8, int16, int32, int64, + uint, uint8, uint16, uint32, uint64, uintptr, + float32, float64: + return OrderedAbsDifference(a, b) // INVALID + case complex64, complex128: + return ComplexAbsDifference(a, b) // INVALID + } +} +``` + +The calls to `OrderedAbsDifference` and `ComplexAbsDifference` are +invalid, because not all the types that implement the `Numeric` +constraint can implement the `OrderedNumeric` or `Complex` +constraints. +Although the type switch means that this code would conceptually work +at run time, there is no support for writing this code at compile +time. +This is another way of expressing one of the omissions listed above: +this design does not provide for specialization. + +## Acknowledgements + +We'd like to thank many people on the Go team, many contributors to +the Go issue tracker, and all the people who have shared their ideas +and their feedback on earlier design drafts. +We read all of it, and we're grateful. + +For this design draft in particular we received detailed feedback from +Josh Bleecher-Snyder, Jon Bodner, Dave Cheney, Jaana Dogan, Kevin +Gillette, Mitchell Hashimoto, Chris Hines, Bill Kennedy, Ayke van +Laethem, Daniel Martí, Elena Morozova, Roger Peppe, and Ronna +Steinberg. + +## Appendix + +This appendix covers various details of the design that don't seem +significant enough to cover in earlier sections. + +### Generic type aliases + +A type alias may refer to a generic type, but the type alias may not +have its own parameters. +This restriction exists because it is unclear how to handle a type +alias with type parameters that have constraints. + +```Go +type VectorAlias = Vector +``` + +In this case uses of the type alias will have to provide type +arguments appropriate for the generic type being aliased. + +```Go +var v VectorAlias(int) +``` + +Type aliases may also refer to instantiated types. + +```Go +type VectorInt = Vector(int) +``` + +### Instantiating a function + +Go normally permits you to refer to a function without passing any +arguments, producing a value of function type. +You may not do this with a function that has type parameters; all type +arguments must be known at compile time. +That said, you can instantiate the function, by passing type +arguments, but you don't have to call the instantiation. +This will produce a function value with no type parameters. + +```Go +// PrintInts is type func([]int). +var PrintInts = Print(int) +``` + +### Embedded type parameter + +When a generic type is a struct, and the type parameter is +embedded as a field in the struct, the name of the field is the name +of the type parameter. + +```Go +// A Lockable is a value that may be safely simultaneously accessed +// from multiple goroutines via the Get and Set methods. +type Lockable(type T) struct { + T + mu sync.Mutex +} + +// Get returns the value stored in a Lockable. +func (l *Lockable(T)) Get() T { + l.mu.Lock() + defer l.mu.Unlock() + return l.T +} + +// Set sets the value in a Lockable. +func (l *Lockable(T)) Set(v T) { + l.mu.Lock() + defer l.mu.Unlock() + l.T = v +} +``` + +### Inline constraints + +As we've seen in examples that use `interface{}` as a type constraint, +it's not necessary for a constraint to use a named interface type. +A type parameter list can use an interface type literal, just as an +ordinary parameter list can use a type literal for a parameter type. + +```Go +// Stringify calls the String method on each element of s, +// and returns the results. +func Stringify(type T interface { String() string })(s []T) (ret []string) { + for _, v := range s { + ret = append(ret, v.String()) + } + return ret +} +``` + +### Type inference for composite literals + +This is a feature we are not suggesting now, but could consider for +later versions of the language. + +We could also consider supporting type inference for composite +literals of generic types. + +```Go +type Pair(type T) struct { f1, f2 T } +var V = Pair{1, 2} // inferred as Pair(int){1, 2} +``` + +It's not clear how often this will arise in real code. + +### Type inference for generic function arguments + +This is a feature we are not suggesting now, but could consider for +later versions of the language. + +In the following example, consider the call to `Find` in `FindClose`. +Type inference can determine that the type argument to `Find` is `T4`, +and from that we know that the type of the final argument must be +`func(T4, T4) bool`, and from that we could deduce that the type +argument to `IsClose` must also be `T4`. +However, the type inference algorithm described earlier cannot do +that, so we must explicitly write `IsClose(T4)`. + +This may seem esoteric at first, but it comes up when passing generic +functions to generic `Map` and `Filter` functions. + +```Go +// Differ has a Diff method that returns how different a value is. +type Differ(type T1) interface { + Diff(T1) int +} + +// IsClose returns whether a and b are close together, based on Diff. +func IsClose(type T2 Differ)(a, b T2) bool { + return a.Diff(b) < 2 +} + +// Find returns the index of the first element in s that matches e, +// based on the cmp function. It returns -1 if no element matches. +func Find(type T3)(s []T3, e T3, cmp func(a, b T3) bool) int { + for i, v := range s { + if cmp(v, e) { + return i + } + } + return -1 +} + +// FindClose returns the index of the first element in s that is +// close to e, based on IsClose. +func FindClose(type T4 Differ)(s []T4, e T4) int { + // With the current type inference algorithm we have to + // explicitly write IsClose(T4) here, although it + // is the only type argument we could possibly use. + return Find(s, e, IsClose(T4)) +} +``` + +### Reflection on type arguments + +Although we don't suggest changing the reflect package, one +possibility to consider for the future would be to add two new +methods to `reflect.Type`: `NumTypeArgument() int` would return the +number of type arguments to a type, and `TypeArgument(i) Type` would +return the i'th type argument. +`NumTypeArgument` would return non-zero for an instantiated generic +type. +Similar methods could be defined for `reflect.Value`, for which +`NumTypeArgument` would return non-zero for an instantiated generic +function. +There might be some kind of programs that would care about this +information. + +### Instantiating types in type literals + +When instantiating a type at the end of a type literal, there is a +parsing ambiguity. + +```Go +x1 := []T(v1) +x2 := []T(v2){} +``` + +In this example, the first case is a type conversion of `v1` to the +type `[]T`. +The second case is a composite literal of type `[]T(v2)`, where `T` is +a generic type that we are instantiating with the type argument `v2`. +The ambiguity is at the point where we see the open parenthesis: at +that point the parser doesn't know whether it is seeing a type +conversion or something like a composite literal. + +To avoid this ambiguity, we require that type instantiations at the +end of a type literal be parenthesized. +To write a type literal that is a slice of a type instantiation, you +must write `[](T(v1))`. +Without those parentheses, `[]T(x)` is parsed as `([]T)(x)`, not as +`[](T(x))`. +This only applies to slice, array, map, chan, and func type literals +ending in a type name. +Of course it is always possible to use a separate type declaration to +give a name to the instantiated type, and to use that. + +### Embedding an instantiated interface type + +There is a parsing ambiguity when embedding an instantiated interface +type in another interface type. + +```Go +type I1(type T) interface { + M(T) +} + +type I2 interface { + I1(int) +} +``` + +In this example we don't know whether interface `I2` has a single +method named `I1` that takes an argument of type `int`, or whether we +are trying to embed the instantiated type `I1(int)` into `I2`. + +For backward compatibility, we treat this as the former case: `I2` has +a method named `I1`. + +In order to embed an instantiated interface type, we require +that extra parentheses be used. + +```Go +type I2 interface { + (I1(int)) +} +``` + +This is currently not permitted by the language, and will be a +relaxation of the existing rules. + +The same applies to embedding an instantiated type in a struct. + +```Go +type S1 struct { + T(int) // field named T of type int +} + +type S2 struct { + (T(int)) // embedded field of type T(int) +} +``` + +The field name of an embedded field of type `T(int)` is simply `T`. |
