|
| 1 | +# Proposal: Go should have generics |
| 2 | + |
| 3 | +Author: [Ian Lance Taylor ]([email protected]) |
| 4 | + |
| 5 | +Created: January 2011 |
| 6 | + |
| 7 | +Last updated: April 2016 |
| 8 | + |
| 9 | +Discussion at https://golang.org/issue/15292 |
| 10 | + |
| 11 | +## Abstract |
| 12 | + |
| 13 | +Go should support some form of generic programming. |
| 14 | +Generic programming enables the representation of algorithms and data |
| 15 | +structures in a generic form, with concrete elements of the code |
| 16 | +(such as types) factored out. |
| 17 | +It means the ability to express algorithms with minimal assumptions |
| 18 | +about data structures, and vice-versa |
| 19 | +(paraphrasing [Jazayeri, et al](http://www.dagstuhl.de/en/program/calendar/semhp/?semnr=98171)). |
| 20 | + |
| 21 | +## Background |
| 22 | + |
| 23 | +### Generic arguments in favor of generics |
| 24 | + |
| 25 | +People can write code once, saving coding time. |
| 26 | +People can fix a bug in one instance without having to remember to fix it |
| 27 | +in others. |
| 28 | +Generics avoid boilerplate: less coding by copying and editing. |
| 29 | + |
| 30 | +Generics save time testing code: they increase the amount of code |
| 31 | +that can be type checked at compile time rather than at run time. |
| 32 | + |
| 33 | +Every statically typed language in current use has generics in one |
| 34 | +form or another (even C has generics, where they are called preprocessor macros; |
| 35 | +[example](https://gcc.gnu.org/viewcvs/gcc/trunk/gcc/vec.h?revision=165314&view=markup&pathrev=165314)). |
| 36 | + |
| 37 | +### Existing support for generic programming in Go |
| 38 | + |
| 39 | +Go already supports a form of generic programming via interfaces. |
| 40 | +People can write an abstract algorithm that works with any type that |
| 41 | +implements the interface. |
| 42 | +However, interfaces are limited because the methods must use specific types. |
| 43 | +There is no way to write an interface with a method that takes an |
| 44 | +argument of type T, for any T, and returns a value of the same type. |
| 45 | +There is no way to write an interface with a method that compares two |
| 46 | +values of the same type T, for any T. |
| 47 | +The assumptions that interfaces require about the types that satisfy |
| 48 | +them are not minimal. |
| 49 | + |
| 50 | +Interfaces are not simply types; they are also values. |
| 51 | +There is no way to use interface types without using interface values, |
| 52 | +and interface values aren’t always efficient. |
| 53 | +There is no way to create a slice of the dynamic type of an interface. |
| 54 | +That is, there is no way to avoid boxing. |
| 55 | + |
| 56 | +### Specific arguments in favor of generics in Go |
| 57 | + |
| 58 | +Generics permit type-safe polymorphic containers. |
| 59 | +Go currently has a very limited set of such containers: slices, and |
| 60 | +maps of most but not all types. |
| 61 | +Not every program can be written using a slice or map. |
| 62 | + |
| 63 | +Look at the functions `SortInts`, `SortFloats`, `SortStrings` in the |
| 64 | +sort package. |
| 65 | +Or `SearchInts`, `SearchFloats`, `SearchStrings`. |
| 66 | +Or the `Len`, `Less`, and `Swap` methods of `byName` in package io/ioutil. |
| 67 | +Pure boilerplate copying. |
| 68 | + |
| 69 | +The `copy` and `append` functions exist because they make slices much |
| 70 | +more useful. |
| 71 | +Generics would mean that these functions are unnecessary. |
| 72 | +Generics would make it possible to write similar functions for maps |
| 73 | +and channels, not to mention user created data types. |
| 74 | +Granted, slices are the most important composite data type, and that’s why |
| 75 | +these functions were needed, but other data types are still useful. |
| 76 | + |
| 77 | +It would be nice to be able to make a copy of a map. |
| 78 | +Right now that function can only be written for a specific map type, |
| 79 | +but, except for types, the same code works for any map type. |
| 80 | +Similarly, it would be nice to be able to multiplex one channel onto |
| 81 | +two, without having to rewrite the function for each channel type. |
| 82 | +One can imagine a range of simple channel manipulators, but they can |
| 83 | +not be written because the type of the channel must be specified |
| 84 | +explicitly. |
| 85 | + |
| 86 | +Generics let people express the relationship between function parameters |
| 87 | +and results. |
| 88 | +Consider the simple Transform function that calls a function on every |
| 89 | +element of a slice, returning a new slice. |
| 90 | +We want to write something like |
| 91 | +``` |
| 92 | +func Transform(s []T, f func(T) U) []U |
| 93 | +``` |
| 94 | +but this can not be expressed in current Go. |
| 95 | + |
| 96 | +In many Go programs, people only have to write explicit types in function |
| 97 | +signatures. |
| 98 | +Without generics, they also have to write them in another place: in the |
| 99 | +type assertion needed to convert from an interface type back to the |
| 100 | +real type. |
| 101 | +The lack of static type checking provided by generics makes the code |
| 102 | +heavier. |
| 103 | + |
| 104 | +### What we want from generics in Go |
| 105 | + |
| 106 | +Any implementation of generics in Go should support the following. |
| 107 | + |
| 108 | +* Define generic types based on types that are not known until they are instantiated. |
| 109 | +* Write algorithms to operate on values of these types. |
| 110 | +* Name generic types and name specific instantiations of generic types. |
| 111 | +* Use types derived from generic types, as in making a slice of a generic type, |
| 112 | + or conversely, given a generic type known to be a slice, defining a variable |
| 113 | + with the slice’s element type. |
| 114 | +* Restrict the set of types that may be used to instantiate a generic type, to |
| 115 | + ensure that the generic type is only instantiated with types that support the |
| 116 | +* required operations. |
| 117 | +* Do not require an explicit relationship between the definition of a generic |
| 118 | + type or function and its use. That is, programs should not have to |
| 119 | + explicitly say *type T implements generic G*. |
| 120 | +* Write interfaces that describe explicit relationships between generic types, |
| 121 | + as in a method that takes two parameters that must both be the same unknown type. |
| 122 | +* Do not require explicit instantiation of generic types or functions; they |
| 123 | + should be instantiated as needed. |
| 124 | + |
| 125 | +### The downsides of generics |
| 126 | + |
| 127 | +Generics affect the whole language. |
| 128 | +It is necessary to evaluate every single language construct to see how |
| 129 | +it will work with generics. |
| 130 | + |
| 131 | +Generics affect the whole standard library. |
| 132 | +It is desirable to have the standard library make effective use of generics. |
| 133 | +Every existing package should be reconsidered to see whether it would benefit |
| 134 | +from using generics. |
| 135 | + |
| 136 | +It becomes tempting to build generics into the standard library at a |
| 137 | +very low level, as in C++ `std::basic_string<char, std::char_traits<char>, std::allocator<char> >`. |
| 138 | +This has its benefits—otherwise nobody would do it—but it has |
| 139 | +wide-ranging and sometimes surprising effects, as in incomprehensible |
| 140 | +C++ error messages. |
| 141 | + |
| 142 | +As [Russ pointed out](http://research.swtch.com/generic), generics are |
| 143 | +a trade off between programmer time, compilation time, and execution |
| 144 | +time. |
| 145 | + |
| 146 | +Go is currently optimizing compilation time and execution time at the |
| 147 | +expense of programmer time. |
| 148 | +Compilation time is a significant benefit of Go. |
| 149 | +Can we retain compilation time benefits without sacrificing too much |
| 150 | +execution time? |
| 151 | + |
| 152 | +Unless we choose to optimize execution time, operations that appear |
| 153 | +cheap may be more expensive if they use values of generic type. |
| 154 | +This may be subtly confusing for programmers. |
| 155 | +I think this is less important for Go than for some other languages, |
| 156 | +as some operations in Go already have hidden costs such as array |
| 157 | +bounds checks. |
| 158 | +Still, it would be essential to ensure that the extra cost of using |
| 159 | +values of generic type is tightly bounded. |
| 160 | + |
| 161 | +Go has a lightweight type system. |
| 162 | +Adding generic types inevitably makes the type system more complex. |
| 163 | +It is essential that the result remain lightweight. |
| 164 | + |
| 165 | +The upsides of the downsides are that Go is a relatively small |
| 166 | +language, and it really is possible to consider every aspect of the |
| 167 | +language when adding generics. |
| 168 | +At least the following sections of the spec would need to be extended: |
| 169 | +Types, Type Identity, Assignability, Type assertions, Calls, Type |
| 170 | +switches, For statements with range clauses. |
| 171 | + |
| 172 | +Only a relatively small number of packages will need to be |
| 173 | +reconsidered in light of generics: container/*, sort, flag, perhaps |
| 174 | +bytes. |
| 175 | +Packages that currently work in terms of interfaces will generally be |
| 176 | +able to continue doing so. |
| 177 | + |
| 178 | +### Conclusion |
| 179 | + |
| 180 | +Generics will make the language safer, more efficient to use, and more |
| 181 | +powerful. |
| 182 | +These advantages are harder to quantify than the disadvantages, but |
| 183 | +they are real. |
| 184 | + |
| 185 | +## Examples of potential uses of generics in Go |
| 186 | + |
| 187 | +* Containers |
| 188 | + * User-written hash tables that are compile-time type-safe, rather than |
| 189 | + converting slice keys to string and using maps |
| 190 | + * Sorted maps (red-black tree or similar) |
| 191 | + * Double-ended queues, circular buffers |
| 192 | + * A simpler Heap |
| 193 | + * `Keys(map[K]V) []K`, `Values(map[K]V) []V` |
| 194 | + * Caches |
| 195 | + * Compile-time type-safe `sync.Pool` |
| 196 | +* Generic algorithms that work with these containers in a type-safe way. |
| 197 | + * Union/Intersection |
| 198 | + * Sort, StableSort, Find |
| 199 | + * Copy (a generic container, and also copy a map) |
| 200 | + * Transform a container by applying a function--LISP `mapcar` and friends |
| 201 | +* math and math/cmplx |
| 202 | +* testing/quick.{`Check`,`CheckEqual`} |
| 203 | +* Mixins |
| 204 | + * like `ioutil.NopCloser`, but preserving other methods instead of |
| 205 | + restricting to the passed-in interface (see the `ReadFoo` variants of |
| 206 | + `bytes.Buffer`) |
| 207 | +* protobuf `proto.Clone` |
| 208 | +* Eliminate boilerplate when calling sort function |
| 209 | +* Generic diff: `func [T] Diff(x, y []T) []range` |
| 210 | +* Channel operations |
| 211 | + * Merge N channels onto one |
| 212 | + * Multiplex one channel onto N |
| 213 | + * The [worker-pool pattern](http://play.golang.org/p/b5XRHnxzZF) |
| 214 | +* Graph algorithms, for example immediate dominator computation |
| 215 | +* Multi-dimensional arrays (not slices) of different lengths |
| 216 | +* Many of the packages in go.text could benefit from it to avoid duplicate |
| 217 | + implementation or APIs for `string` and `[]byte` variants; many points that |
| 218 | + could benefit need high performance, though, and generics should provide that |
| 219 | + benefit |
| 220 | + |
| 221 | +## Proposal |
| 222 | + |
| 223 | +I won’t discuss a specific implementation proposal here: my hope is |
| 224 | +that this document helps show people that generics are worth having |
| 225 | +provided the downsides can be kept under control. |
| 226 | + |
| 227 | +The following documents are my previous generics proposals, |
| 228 | +presented for historic reference. All are flawed in various ways. |
| 229 | + |
| 230 | +* [Type functions](NNNN/2010-06-type-functions.md) (June 2010) |
| 231 | +* [Generalized types](NNNN/2011-03-gen.md) (March 2011) |
| 232 | +* [Generalized types](NNNN/2013-10-gen.md) (October 2013) |
| 233 | +* [Type parameters](NNNN/2013-12-type-params.md) (December 2013) |
0 commit comments