Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1 | # Contracts — Draft Design |
| 2 | |
| 3 | Ian Lance Taylor\ |
| 4 | Robert Griesemer\ |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 5 | July 31, 2019 |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 6 | |
Ian Lance Taylor | e022bce | 2020-06-16 12:12:21 -0700 | [diff] [blame] | 7 | ## Superseded |
| 8 | |
| 9 | We will not be pursuing the approach outlined in this design draft. |
Ian Lance Taylor | 78bd525 | 2021-03-19 11:43:34 -0700 | [diff] [blame] | 10 | It has been replaced by a [new |
| 11 | proposal](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md). |
Ian Lance Taylor | e022bce | 2020-06-16 12:12:21 -0700 | [diff] [blame] | 12 | This document exists for historical context. |
| 13 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 14 | ## Abstract |
| 15 | |
| 16 | We suggest extending the Go language to add optional type parameters |
| 17 | to types and functions. |
| 18 | Type parameters may be constrained by contracts: they may be used as |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 19 | ordinary types that only support the operations permitted by the |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 20 | contracts. |
| 21 | Type inference via a unification algorithm is supported to permit |
| 22 | omitting type arguments from function calls in many cases. |
| 23 | Depending on a detail, the design can be fully backward compatible |
| 24 | with Go 1. |
| 25 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 26 | ## Background |
| 27 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 28 | This version of the design draft is similar to the one presented on |
| 29 | August 27, 2018, except that the syntax of contracts is completely |
| 30 | different. |
| 31 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 32 | There have been many [requests to add additional support for generic |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 33 | programming](https://github.com/golang/go/wiki/ExperienceReports#generics) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 34 | in Go. |
Andrew Bonventre | d41bf0f | 2019-07-29 16:25:31 -0400 | [diff] [blame] | 35 | There has been extensive discussion on |
| 36 | [the issue tracker](https://golang.org/issue/15292) and on |
| 37 | [a living document](https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view). |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 38 | |
| 39 | There have been several proposals for adding type parameters, which |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 40 | can be found through the links above. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 41 | Many of the ideas presented here have appeared before. |
| 42 | The main new features described here are the syntax and the careful |
| 43 | examination of contracts. |
| 44 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 45 | This design draft suggests extending the Go language to add a form of |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 46 | parametric polymorphism, where the type parameters are bounded not by |
| 47 | a subtyping relationship but by explicitly defined structural |
| 48 | constraints. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 49 | Among other languages that support parameteric polymorphism this |
| 50 | design is perhaps most similar to Ada, although the syntax is |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 51 | completely different. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 52 | |
| 53 | This design does not support template metaprogramming or any other |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 54 | form of compile time programming. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 55 | |
| 56 | As the term _generic_ is widely used in the Go community, we will |
| 57 | use it below as a shorthand to mean a function or type that takes type |
| 58 | parameters. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 59 | Don't confuse the term generic as used in this design with the same |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 60 | term in other languages like C++, C#, Java, or Rust; they have |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 61 | similarities but are not the same. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 62 | |
| 63 | ## Design |
| 64 | |
| 65 | We will describe the complete design in stages based on examples. |
| 66 | |
| 67 | ### Type parameters |
| 68 | |
| 69 | Generic code is code that is written using types that will be |
| 70 | specified later. |
| 71 | Each unspecified type is called a _type parameter_. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 72 | When running the generic code, the type parameter will be set to a |
| 73 | _type argument_. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 74 | |
| 75 | Here is a function that prints out each element of a slice, where the |
| 76 | element type of the slice, here called `T`, is unknown. |
| 77 | This is a trivial example of the kind of function we want to permit in |
| 78 | order to support generic programming. |
| 79 | |
| 80 | ```Go |
| 81 | // Print prints the elements of a slice. |
| 82 | // It should be possible to call this with any slice value. |
| 83 | func Print(s []T) { // Just an example, not the suggested syntax. |
| 84 | for _, v := range s { |
| 85 | fmt.Println(v) |
| 86 | } |
| 87 | } |
| 88 | ``` |
| 89 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 90 | With this approach, the first decision to make is: how should the type |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 91 | parameter `T` be declared? |
| 92 | In a language like Go, we expect every identifier to be declared in |
| 93 | some way. |
| 94 | |
| 95 | Here we make a design decision: type parameters are similar to |
| 96 | ordinary non-type function parameters, and as such should be listed |
| 97 | along with other parameters. |
| 98 | However, type parameters are not the same as non-type parameters, so |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 99 | although they appear in the list of parameters we want to distinguish |
| 100 | them. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 101 | That leads to our next design decision: we define an additional, |
| 102 | optional, parameter list, describing type parameters. |
| 103 | This parameter list appears before the regular parameters. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 104 | It starts with the keyword `type`, and lists type parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 105 | |
| 106 | ```Go |
| 107 | func Print(type T)(s []T) { |
| 108 | // same as above |
| 109 | } |
| 110 | ``` |
| 111 | |
| 112 | This says that within the function `Print` the identifier `T` is a |
| 113 | type parameter, a type that is currently unknown but that will be |
| 114 | known when the function is called. |
| 115 | |
| 116 | Since `Print` has a type parameter, when we call it we must pass a |
| 117 | type argument. |
| 118 | Type arguments are passed much like type parameters are declared: as a |
| 119 | separate list of arguments. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 120 | At the call site, the `type` keyword is not required. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 121 | |
| 122 | ```Go |
| 123 | Print(int)([]int{1, 2, 3}) |
| 124 | ``` |
| 125 | |
| 126 | ### Type contracts |
| 127 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 128 | Let's make our example slightly more complicated. |
| 129 | Let's turn it into a function that converts a slice of any type into a |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 130 | `[]string` by calling a `String` method on each element. |
| 131 | |
| 132 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 133 | // This function is INVALID. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 134 | func Stringify(type T)(s []T) (ret []string) { |
| 135 | for _, v := range s { |
| 136 | ret = append(ret, v.String()) // INVALID |
| 137 | } |
| 138 | return ret |
| 139 | } |
| 140 | ``` |
| 141 | |
| 142 | This might seem OK at first glance, but in this example, `v` has type |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 143 | `T`, and we don't know anything about `T`. |
| 144 | In particular, we don't know that `T` has a `String` method. |
| 145 | So the call to `v.String()` is invalid. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 146 | |
| 147 | Naturally, the same issue arises in other languages that support |
| 148 | generic programming. |
| 149 | In C++, for example, a generic function (in C++ terms, a function |
| 150 | template) can call any method on a value of generic type. |
| 151 | That is, in the C++ approach, calling `v.String()` is fine. |
| 152 | If the function is called with a type that does not have a `String` |
| 153 | method, the error is reported at the point of the function call. |
| 154 | These errors can be lengthy, as there may be several layers of generic |
| 155 | function calls before the error occurs, all of which must be reported |
| 156 | for complete clarity. |
| 157 | |
| 158 | The C++ approach would be a poor choice for Go. |
| 159 | One reason is the style of the language. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 160 | In Go we don't refer to names, such as, in this case, `String`, and |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 161 | hope that they exist. |
| 162 | Go resolves all names to their declarations when they are seen. |
| 163 | |
| 164 | Another reason is that Go is designed to support programming at |
| 165 | scale. |
| 166 | We must consider the case in which the generic function definition |
| 167 | (`Stringify`, above) and the call to the generic function (not shown, |
| 168 | but perhaps in some other package) are far apart. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 169 | In general, all generic code implies a contract that type arguments |
| 170 | need to implement. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 171 | In this case, the contract is pretty obvious: the type has to have a |
| 172 | `String() string` method. |
| 173 | In other cases it may be much less obvious. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 174 | We don't want to derive the contract from whatever `Stringify` happens |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 175 | to do. |
| 176 | If we did, a minor change to `Stringify` might change the contract. |
| 177 | That would mean that a minor change could cause code far away, that |
| 178 | calls the function, to unexpectedly break. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 179 | It's fine for `Stringify` to deliberately change its contract, and |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 180 | force users to change. |
| 181 | What we want to avoid is `Stringify` changing its contract |
| 182 | accidentally. |
| 183 | |
| 184 | This is an important rule that we believe should apply to any attempt |
| 185 | to define generic programming in Go: there should be an explicit |
| 186 | contract between the generic code and calling code. |
| 187 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 188 | ### Contract introduction |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 189 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 190 | In this design, a contract describes the requirements of a set of |
| 191 | types. |
| 192 | We'll discuss contracts further later, but for now we'll just say that |
| 193 | one of the things that a contract can do is specify that a type |
| 194 | argument must implement a particular method. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 195 | |
| 196 | For the `Stringify` example, we need to write a contract that says |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 197 | that the single type parameter has a `String` method that takes no |
| 198 | arguments and returns a value of type `string`. |
| 199 | We write that like this: |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 200 | |
| 201 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 202 | contract stringer(T) { |
| 203 | T String() string |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 204 | } |
| 205 | ``` |
| 206 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 207 | A contract is introduced with a new keyword `contract`, followed by a |
| 208 | name and a list of identifiers. |
| 209 | The identifiers name the types that the contract will specify. |
| 210 | Specifying a required method looks like defining a method in an |
| 211 | interface type, except that the receiver type must be explicitly |
| 212 | provided. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 213 | |
| 214 | ### Using a contract to verify type arguments |
| 215 | |
| 216 | A contract serves two purposes. |
| 217 | First, contracts are used to validate a set of type arguments. |
| 218 | As shown above, when a function with type parameters is called, it |
| 219 | will be called with a set of type arguments. |
| 220 | When the compiler sees the function call, it will use the contract to |
| 221 | validate the type arguments. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 222 | If the type arguments don't satisfy the requirements specified by the |
| 223 | contract, the compiler will report a type error: the call is using |
| 224 | types that the function's contract does not permit. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 225 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 226 | The `stringer` contract seen earlier requires that the type argument |
| 227 | used for `T` has a `String` method that takes no arguments and |
| 228 | returns a value of type `string`. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 229 | |
| 230 | ### The party of the second part |
| 231 | |
| 232 | A contract is not used only at the call site. |
| 233 | It is also used to describe what the function using the contract, the |
| 234 | function with type parameters, is permitted to do with those type |
| 235 | parameters. |
| 236 | |
| 237 | In a function with type parameters that does not use a contract, such |
| 238 | as the `Print` example shown earlier, the function is only permitted |
| 239 | to use those type parameters in ways that any type may be used in Go. |
| 240 | That is, operations like: |
| 241 | |
| 242 | * declare variables of those types |
| 243 | * assign other values of the same type to those variables |
| 244 | * pass those variables to functions or return them from functions |
| 245 | * take the address of those variables |
| 246 | * define and use other types that use those types, such as a slice of |
| 247 | that type |
| 248 | |
| 249 | If the function wants to take any more specific action with the type |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 250 | parameter, or a value of the type parameter, the contract must |
| 251 | explicitly support that action. |
| 252 | In the `stringer` example seen earlier, the contract provides the |
| 253 | ability to call a method `String` on a value of the type parameter. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 254 | That is, naturally, exactly the operation that the `Stringify` |
| 255 | function needs. |
| 256 | |
| 257 | ### Using a contract |
| 258 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 259 | We've seen how the `stringer` contract can be used to verify that a |
| 260 | type argument is suitable for the `Stringify` function, and we've seen |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 261 | how the contract permits the `Stringify` function to call the `String` |
| 262 | method that it needs. |
| 263 | The final step is showing how the `Stringify` function uses the |
| 264 | `stringer` contract. |
| 265 | This is done by naming the contract at the end of the list of type |
| 266 | parameters. |
| 267 | |
| 268 | ```Go |
| 269 | func Stringify(type T stringer)(s []T) (ret []string) { |
| 270 | for _, v := range s { |
| 271 | ret = append(ret, v.String()) // now valid |
| 272 | } |
| 273 | return ret |
| 274 | } |
| 275 | ``` |
| 276 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 277 | The list of type parameters (in this case, a list with the single |
| 278 | element `T`) is followed by an optional contract name. |
| 279 | When just the contract name is listed, as above, the contract must |
| 280 | have the same number of parameters as the function has type |
| 281 | parameters; when validating the contract, the type parameters are |
| 282 | passed to the contract in the order in which they appear in the |
| 283 | function signature. |
| 284 | Later we'll discuss passing explicit type parameters to the contract. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 285 | |
| 286 | ### Multiple type parameters |
| 287 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 288 | Although the examples we've seen so far use only a single type |
| 289 | parameter, functions may have multiple type parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 290 | |
| 291 | ```Go |
| 292 | func Print2(type T1, T2)(s1 []T1, s2 []T2) { ... } |
| 293 | ``` |
| 294 | |
| 295 | Compare this to |
| 296 | |
| 297 | ```Go |
| 298 | func Print2Same(type T1)(s1 []T1, s2 []T1) { ... } |
| 299 | ``` |
| 300 | |
| 301 | In `Print2` `s1` and `s2` may be slices of different types. |
| 302 | In `Print2Same` `s1` and `s2` must be slices of the same element |
| 303 | type. |
| 304 | |
| 305 | Although functions may have multiple type parameters, they may only |
| 306 | have a single contract. |
| 307 | |
| 308 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 309 | contract viaStrings(To, From) { |
| 310 | To Set(string) |
| 311 | From String() string |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 312 | } |
| 313 | |
| 314 | func SetViaStrings(type To, From viaStrings)(s []From) []To { |
| 315 | r := make([]To, len(s)) |
| 316 | for i, v := range s { |
| 317 | r[i].Set(v.String()) |
| 318 | } |
| 319 | return r |
| 320 | } |
| 321 | ``` |
| 322 | |
| 323 | ### Parameterized types |
| 324 | |
| 325 | We want more than just generic functions: we also want generic types. |
| 326 | We suggest that types be extended to take type parameters. |
| 327 | |
| 328 | ```Go |
| 329 | type Vector(type Element) []Element |
| 330 | ``` |
| 331 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 332 | A type's parameters are just like a function's type parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 333 | |
| 334 | Within the type definition, the type parameters may be used like any |
| 335 | other type. |
| 336 | |
| 337 | To use a parameterized type, you must supply type arguments. |
| 338 | This looks like a function call, except that the function in this case |
| 339 | is actually a type. |
| 340 | This is called _instantiation_. |
| 341 | |
| 342 | ```Go |
| 343 | var v Vector(int) |
| 344 | ``` |
| 345 | |
| 346 | Parameterized types can have methods. |
| 347 | The receiver type of a method must list the type parameters. |
| 348 | They are listed without the `type` keyword or any contract. |
| 349 | |
| 350 | ```Go |
| 351 | func (v *Vector(Element)) Push(x Element) { *v = append(*v, x) } |
| 352 | ``` |
| 353 | |
| 354 | A parameterized type can refer to itself in cases where a type can |
| 355 | ordinarily refer to itself, but when it does so the type arguments |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 356 | must be the type parameters, listed in the same order. |
| 357 | This restriction prevents infinite recursion of type instantiation. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 358 | |
| 359 | ```Go |
| 360 | // This is OK. |
| 361 | type List(type Element) struct { |
| 362 | next *List(Element) |
| 363 | val Element |
| 364 | } |
| 365 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 366 | // This type is INVALID. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 367 | type P(type Element1, Element2) struct { |
| 368 | F *P(Element2, Element1) // INVALID; must be (Element1, Element2) |
| 369 | } |
| 370 | ``` |
| 371 | |
| 372 | (Note: with more understanding of how people want to write code, it |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 373 | may be possible to relax this rule to permit some cases that use |
| 374 | different type arguments.) |
| 375 | |
| 376 | The type parameter of a parameterized type may have contracts. |
| 377 | |
| 378 | ```Go |
| 379 | type StringableVector(type T stringer) []T |
| 380 | |
| 381 | func (s StringableVector(T)) String() string { |
| 382 | var sb strings.Builder |
| 383 | sb.WriteString("[") |
| 384 | for i, v := range s { |
| 385 | if i > 0 { |
| 386 | sb.WriteString(", ") |
| 387 | } |
| 388 | sb.WriteString(v.String()) |
| 389 | } |
| 390 | sb.WriteString("]") |
| 391 | return sb.String() |
| 392 | } |
| 393 | ``` |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 394 | |
| 395 | When a parameterized type is a struct, and the type parameter is |
| 396 | embedded as a field in the struct, the name of the field is the name |
| 397 | of the type parameter, not the name of the type argument. |
| 398 | |
| 399 | ```Go |
| 400 | type Lockable(type T) struct { |
| 401 | T |
| 402 | mu sync.Mutex |
| 403 | } |
| 404 | |
| 405 | func (l *Lockable(T)) Get() T { |
| 406 | l.mu.Lock() |
| 407 | defer l.mu.Unlock() |
| 408 | return l.T |
| 409 | } |
| 410 | ``` |
| 411 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 412 | ### Parameterized type aliases |
| 413 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 414 | Type aliases may not have parameters. |
| 415 | This restriction exists because it is unclear how to handle a type |
| 416 | alias with type parameters that have a contract. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 417 | |
| 418 | Type aliases may refer to instantiated types. |
| 419 | |
| 420 | ```Go |
| 421 | type VectorInt = Vector(int) |
| 422 | ``` |
| 423 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 424 | If a type alias refers to a parameterized type, it must provide type |
| 425 | arguments. |
| 426 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 427 | ### Methods may not take additional type arguments |
| 428 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 429 | Although methods of a parameterized type may use the type's |
| 430 | parameters, methods may not themselves have additional type |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 431 | parameters. |
| 432 | Where it would be useful to add type arguments to a method, people |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 433 | will have to write a suitably parameterized top-level function. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 434 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 435 | This restriction avoids having to specify the details of exactly when |
| 436 | a method with type arguments implements an interface. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 437 | (This is a feature that can perhaps be added later if it proves |
| 438 | necessary.) |
| 439 | |
| 440 | ### Contract embedding |
| 441 | |
| 442 | A contract may embed another contract, by listing it in the |
| 443 | contract body with type arguments. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 444 | This will look a bit like a method definition in the contract body, |
| 445 | but it will be different because there will be no receiver type. |
| 446 | It is handled as if the embedded contract's body were placed into the |
| 447 | calling contract, with the embedded contract's type parameters |
| 448 | replaced by the embedded type arguments. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 449 | |
| 450 | This contract embeds the contract `stringer` defined earlier. |
| 451 | |
| 452 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 453 | contract PrintStringer(X) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 454 | stringer(X) |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 455 | X Print() |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 456 | } |
| 457 | ``` |
| 458 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 459 | This is equivalent to |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 460 | |
| 461 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 462 | contract PrintStringer(X) { |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 463 | X String() string |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 464 | X Print() |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 465 | } |
| 466 | ``` |
| 467 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 468 | ### Using types that refer to themselves in contracts |
| 469 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 470 | Although this is implied by what has already been discussed, it's |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 471 | worth pointing out explicitly that a contract may require a method to |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 472 | have an argument whose type is the same as the method's receiver |
| 473 | type. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 474 | |
| 475 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 476 | package compare |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 477 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 478 | // The equal contract describes types that have an Equal method with |
| 479 | // an argument of the same type as the receiver type. |
| 480 | contract equal(T) { |
| 481 | T Equal(T) bool |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 482 | } |
| 483 | |
| 484 | // Index returns the index of e in s, or -1. |
| 485 | func Index(type T equal)(s []T, e T) int { |
| 486 | for i, v := range s { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 487 | // Both e and v are type T, so it's OK to call e.Equal(v). |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 488 | if e.Equal(v) { |
| 489 | return i |
| 490 | } |
| 491 | } |
| 492 | return -1 |
| 493 | } |
| 494 | ``` |
| 495 | |
| 496 | This function can be used with any type that has an `Equal` method |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 497 | whose single parameter type is the same as the receiver type. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 498 | |
| 499 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 500 | import "compare" |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 501 | |
| 502 | type EqualInt int |
| 503 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 504 | // The Equal method lets EqualInt satisfy the compare.equal contract. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 505 | func (a EqualInt) Equal(b EqualInt) bool { return a == b } |
| 506 | |
| 507 | func Index(s []EqualInt, e EqualInt) int { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 508 | return compare.Index(EqualInt)(s, e) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 509 | } |
| 510 | ``` |
| 511 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 512 | In this example, when we pass `EqualInt` to `compare.Index`, we |
| 513 | check whether `EqualInt` satisfies the contract `compare.equal`. |
| 514 | We replace `T` with `EqualInt` in the declaration of the `Equal` |
| 515 | method in the `equal` contract, and see whether `EqualInt` has a |
| 516 | matching method. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 517 | `EqualInt` has a method `Equal` that accepts a parameter of type |
| 518 | `EqualInt`, so all is well, and the compilation succeeds. |
| 519 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 520 | ### Mutually referencing type parameters |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 521 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 522 | Within a contract, methods may refer to any of the contract's type |
| 523 | parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 524 | |
| 525 | For example, consider a generic graph package that contains generic |
| 526 | algorithms that work with graphs. |
| 527 | The algorithms use two types, `Node` and `Edge`. |
| 528 | `Node` is expected to have a method `Edges() []Edge`. |
| 529 | `Edge` is expected to have a method `Nodes() (Node, Node)`. |
| 530 | A graph can be represented as a `[]Node`. |
| 531 | |
| 532 | This simple representation is enough to implement graph algorithms |
| 533 | like finding the shortest path. |
| 534 | |
| 535 | ```Go |
| 536 | package graph |
| 537 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 538 | contract G(Node, Edge) { |
| 539 | Node Edges() []Edge |
| 540 | Edge Nodes() (from Node, to Node) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 541 | } |
| 542 | |
| 543 | type Graph(type Node, Edge G) struct { ... } |
| 544 | func New(type Node, Edge G)(nodes []Node) *Graph(Node, Edge) { ... } |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 545 | func (g *Graph(Node, Edge)) ShortestPath(from, to Node) []Edge { ... } |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 546 | ``` |
| 547 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 548 | While at first glance this may look like a typical use of interface |
| 549 | types, `Node` and `Edge` are non-interface types with specific |
| 550 | methods. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 551 | In order to use `graph.Graph`, the type arguments used for `Node` and |
| 552 | `Edge` have to define methods that follow a certain pattern, but they |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 553 | don't have to actually use interface types to do so. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 554 | |
| 555 | For example, consider these type definitions in some other package: |
| 556 | |
| 557 | ```Go |
| 558 | type Vertex struct { ... } |
| 559 | func (v *Vertex) Edges() []*FromTo { ... } |
| 560 | type FromTo struct { ... } |
Christian Himpel | bf0f39f | 2019-07-30 11:14:26 +0200 | [diff] [blame] | 561 | func (ft *FromTo) Nodes() (*Vertex, *Vertex) { ... } |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 562 | ``` |
| 563 | |
| 564 | There are no interface types here, but we can instantiate |
| 565 | `graph.Graph` using the type arguments `*Vertex` and `*FromTo`: |
| 566 | |
| 567 | ```Go |
| 568 | var g = graph.New(*Vertex, *FromTo)([]*Vertex{ ... }) |
| 569 | ``` |
| 570 | |
| 571 | `*Vertex` and `*FromTo` are not interface types, but when used |
| 572 | together they define methods that implement the contract `graph.G`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 573 | Note that we couldn't use plain `Vertex` or `FromTo`, since the |
| 574 | required methods are pointer methods, not value methods. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 575 | |
| 576 | Although `Node` and `Edge` do not have to be instantiated with |
| 577 | interface types, it is also OK to use interface types if you like. |
| 578 | |
| 579 | ```Go |
| 580 | type NodeInterface interface { Edges() []EdgeInterface } |
| 581 | type EdgeInterface interface { Nodes() (NodeInterface, NodeInterface) } |
| 582 | ``` |
| 583 | |
| 584 | We could instantiate `graph.Graph` with the types `NodeInterface` and |
| 585 | `EdgeInterface`, since they implement the `graph.G` contract. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 586 | There isn't much reason to instantiate a type this way, but it is |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 587 | permitted. |
| 588 | |
| 589 | This ability for type parameters to refer to other type parameters |
| 590 | illustrates an important point: it should be a requirement for any |
| 591 | attempt to add generics to Go that it be possible to instantiate |
| 592 | generic code with multiple type arguments that refer to each other in |
| 593 | ways that the compiler can check. |
| 594 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 595 | As it is a common observation that contracts share some |
| 596 | characteristics of interface types, it's worth stressing that this |
| 597 | capability is one that contracts provide but interface types do not. |
| 598 | |
| 599 | ### Passing parameters to a contract |
| 600 | |
| 601 | As mentioned earlier, by default the type parameters are passed to the |
| 602 | contract in the order in which they appear in the function signature. |
| 603 | It is also possible to explicitly pass type parameters to a contract |
| 604 | as though they were arguments. |
| 605 | This is useful if the contract and the generic function take type |
| 606 | parameters in a different order, or if only some parameters need a |
| 607 | contract. |
| 608 | |
| 609 | In this example the type parameter `E` can be any type, but the type |
| 610 | parameter `M` must implement the `String` method. |
| 611 | The function passes just `M` to the `stringer` contract, leaving `E` |
| 612 | as though it had no constraints. |
| 613 | |
| 614 | ```Go |
| 615 | func MapAndPrint(type E, M stringer(M))(s []E, f(E) M) []string { |
| 616 | r := make([]string, len(s)) |
| 617 | for i, v := range s { |
| 618 | r[i] = f(v).String() |
| 619 | } |
| 620 | return r |
| 621 | } |
| 622 | ``` |
| 623 | |
| 624 | ### Contract syntactic details |
| 625 | |
| 626 | Contracts may only appear at the top level of a package. |
| 627 | |
| 628 | While contracts could be defined to work within the body of a |
| 629 | function, it's hard to think of realistic examples in which they would |
| 630 | be useful. |
| 631 | We see this as similar to the way that methods can not be defined |
| 632 | within the body of a function. |
| 633 | A minor point is that only permitting contracts at the top level |
| 634 | permits the design to be Go 1 compatible. |
| 635 | |
| 636 | There are a few ways to handle the syntax: |
| 637 | |
| 638 | * We could make `contract` be a keyword only at the start of a |
| 639 | top-level declaration, and otherwise be a normal identifier. |
| 640 | * We could declare that if you use `contract` at the start of a |
| 641 | top-level declaration, then it becomes a keyword for the entire |
| 642 | package. |
| 643 | * We could make `contract` always be a keyword, albeit one that can |
| 644 | only appear in one place, in which case this design is not Go 1 |
| 645 | compatible. |
| 646 | |
| 647 | Like other top level declarations, a contract is exported if its name |
| 648 | starts with an uppercase letter. |
| 649 | If exported it may be used by functions, types, or contracts in other |
| 650 | packages. |
| 651 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 652 | ### Values of type parameters are not boxed |
| 653 | |
| 654 | In the current implementations of Go, interface values always hold |
| 655 | pointers. |
| 656 | Putting a non-pointer value in an interface variable causes the value |
| 657 | to be _boxed_. |
| 658 | That means that the actual value is stored somewhere else, on the heap |
| 659 | or stack, and the interface value holds a pointer to that location. |
| 660 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 661 | In this design, values of generic types are not boxed. |
| 662 | For example, let's consider a function that works for any type `T` |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 663 | with a `Set(string)` method that initializes the value based on a |
| 664 | string, and uses it to convert a slice of `string` to a slice of `T`. |
| 665 | |
| 666 | ```Go |
| 667 | package from |
| 668 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 669 | contract setter(T) { |
| 670 | T Set(string) error |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 671 | } |
| 672 | |
| 673 | func Strings(type T setter)(s []string) ([]T, error) { |
| 674 | ret := make([]T, len(s)) |
| 675 | for i, v := range s { |
| 676 | if err := ret[i].Set(v); err != nil { |
| 677 | return nil, err |
| 678 | } |
| 679 | } |
| 680 | return ret, nil |
| 681 | } |
| 682 | ``` |
| 683 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 684 | Now let's see some code in a different package. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 685 | |
| 686 | ```Go |
| 687 | type Settable int |
| 688 | |
| 689 | func (p *Settable) Set(s string) (err error) { |
| 690 | *p, err = strconv.Atoi(s) |
| 691 | return err |
| 692 | } |
| 693 | |
| 694 | func F() { |
| 695 | // The type of nums is []Settable. |
| 696 | nums, err := from.Strings(Settable)([]string{"1", "2"}) |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 697 | if err != nil { ... } |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 698 | // Settable can be converted directly to int. |
| 699 | // This will set first to 1. |
| 700 | first := int(nums[0]) |
| 701 | ... |
| 702 | } |
| 703 | ``` |
| 704 | |
| 705 | When we call `from.Strings` with the type `Settable` we get back a |
| 706 | `[]Settable` (and an error). |
| 707 | The values in that slice will be `Settable` values, which is to say, |
| 708 | they will be integers. |
| 709 | They will not be boxed as pointers, even though they were created and |
| 710 | set by a generic function. |
| 711 | |
| 712 | Similarly, when a parameterized type is instantiated it will have the |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 713 | expected types as components. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 714 | |
| 715 | ```Go |
| 716 | package pair |
| 717 | |
| 718 | type Pair(type carT, cdrT) struct { |
| 719 | f1 carT |
| 720 | f2 cdrT |
| 721 | } |
| 722 | ``` |
| 723 | |
| 724 | When this is instantiated, the fields will not be boxed, and no |
| 725 | unexpected memory allocations will occur. |
| 726 | The type `pair.Pair(int, string)` is convertible to `struct { f1 int; |
| 727 | f2 string }`. |
| 728 | |
| 729 | ### Function argument type inference |
| 730 | |
| 731 | In many cases, when calling a function with type parameters, we can |
| 732 | use type inference to avoid having to explicitly write out the type |
| 733 | arguments. |
| 734 | |
| 735 | Go back to the example of a call to our simple `Print` function: |
| 736 | |
| 737 | ```Go |
| 738 | Print(int)([]int{1, 2, 3}) |
| 739 | ``` |
| 740 | |
| 741 | The type argument `int` in the function call can be inferred from the |
| 742 | type of the non-type argument. |
| 743 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 744 | This can only be done when all the function's type parameters are used |
| 745 | for the types of the function's (non-type) input parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 746 | If there are some type parameters that are used only for the |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 747 | function's result parameter types, or only in the body of the |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 748 | function, then it is not possible to infer the type arguments for the |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 749 | function, since there is no value from which to infer the types. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 750 | For example, when calling `from.Strings` as defined earlier, the type |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 751 | parameters cannot be inferred because the function's type parameter |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 752 | `T` is not used for an input parameter, only for a result. |
| 753 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 754 | When the function's type arguments can be inferred, the language uses |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 755 | type unification. |
| 756 | On the caller side we have the list of types of the actual (non-type) |
| 757 | arguments, which for the `Print` example here is simply `[]int`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 758 | On the function side is the list of the types of the function's |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 759 | non-type parameters, which here is `[]T`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 760 | In the lists, we discard respective arguments for which the function |
| 761 | side does not use a type parameter. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 762 | We must then unify the remaining argument types. |
| 763 | |
| 764 | Type unification is a two pass algorithm. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 765 | In the first pass, we ignore untyped constants on the caller side and |
| 766 | their corresponding types in the function definition. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 767 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 768 | We compare corresponding types in the lists. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 769 | Their structure must be identical, except that type parameters on the |
| 770 | function side match the type that appears on the caller side at the |
| 771 | point where the type parameter occurs. |
| 772 | If the same type parameter appears more than once on the function |
| 773 | side, it will match multiple argument types on the caller side. |
| 774 | Those caller types must be identical, or type unification fails, and |
| 775 | we report an error. |
| 776 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 777 | After the first pass, we check any untyped constants on the caller |
| 778 | side. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 779 | If there are no untyped constants, or if the type parameters in the |
| 780 | corresponding function types have matched other input types, then |
| 781 | type unification is complete. |
| 782 | |
| 783 | Otherwise, for the second pass, for any untyped constants whose |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 784 | corresponding function types are not yet set, we determine the default |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 785 | type of the untyped constant in [the usual |
| 786 | way](https://golang.org/ref/spec#Constants). |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 787 | Then we run the type unification algorithm again, this time with no |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 788 | untyped constants. |
| 789 | |
| 790 | In this example |
| 791 | |
| 792 | ```Go |
| 793 | s1 := []int{1, 2, 3} |
| 794 | Print(s1) |
| 795 | ``` |
| 796 | |
| 797 | we compare `[]int` with `[]T`, match `T` with `int`, and we are done. |
| 798 | The single type parameter `T` is `int`, so we infer that the call |
| 799 | to `Print` is really a call to `Print(int)`. |
| 800 | |
| 801 | For a more complex example, consider |
| 802 | |
| 803 | ```Go |
| 804 | package transform |
| 805 | |
| 806 | func Slice(type From, To)(s []From, f func(From) To) []To { |
| 807 | r := make([]To, len(s)) |
| 808 | for i, v := range s { |
| 809 | r[i] = f(v) |
| 810 | } |
| 811 | return r |
| 812 | } |
| 813 | ``` |
| 814 | |
| 815 | The two type parameters `From` and `To` are both used for input |
| 816 | parameters, so type inference is possible. |
| 817 | In the call |
| 818 | |
| 819 | ```Go |
| 820 | strs := transform.Slice([]int{1, 2, 3}, strconv.Itoa) |
| 821 | ``` |
| 822 | |
| 823 | we unify `[]int` with `[]From`, matching `From` with `int`. |
| 824 | We unify the type of `strconv.Itoa`, which is `func(int) string`, |
| 825 | with `func(From) To`, matching `From` with `int` and `To` with |
| 826 | `string`. |
| 827 | `From` is matched twice, both times with `int`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 828 | Unification succeeds, so the call written as `transform.Slice` is a |
| 829 | call of `transform.Slice(int, string)`. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 830 | |
| 831 | To see the untyped constant rule in effect, consider |
| 832 | |
| 833 | ```Go |
| 834 | package pair |
| 835 | |
| 836 | func New(type T)(f1, f2 T) *Pair(T) { ... } |
| 837 | ``` |
| 838 | |
| 839 | In the call `pair.New(1, 2)` both arguments are untyped constants, so |
| 840 | both are ignored in the first pass. |
| 841 | There is nothing to unify. |
| 842 | We still have two untyped constants after the first pass. |
| 843 | Both are set to their default type, `int`. |
| 844 | The second run of the type unification pass unifies `T` with `int`, |
| 845 | so the final call is `pair.New(int)(1, 2)`. |
| 846 | |
| 847 | In the call `pair.New(1, int64(2))` the first argument is an untyped |
| 848 | constant, so we ignore it in the first pass. |
| 849 | We then unify `int64` with `T`. |
| 850 | At this point the type parameter corresponding to the untyped constant |
| 851 | is fully determined, so the final call is `pair.New(int64)(1, int64(2))`. |
| 852 | |
| 853 | In the call `pair.New(1, 2.5)` both arguments are untyped constants, |
| 854 | so we move on the second pass. |
| 855 | This time we set the first constant to `int` and the second to |
| 856 | `float64`. |
| 857 | We then try to unify `T` with both `int` and `float64`, so |
| 858 | unification fails, and we report a compilation error. |
| 859 | |
| 860 | Note that type inference is done without regard to contracts. |
| 861 | First we use type inference to determine the type arguments to use for |
| 862 | the package, and then, if that succeeds, we check whether those type |
| 863 | arguments implement the contract. |
| 864 | |
| 865 | Note that after successful type inference, the compiler must still |
| 866 | check that the arguments can be assigned to the parameters, as for any |
| 867 | function call. |
| 868 | This need not be the case when untyped constants are involved. |
| 869 | |
| 870 | (Note: Type inference is a convenience feature. |
| 871 | Although we think it is an important feature, it does not add any |
| 872 | functionality to the design, only convenience in using it. |
| 873 | It would be possible to omit it from the initial implementation, and |
| 874 | see whether it seems to be needed. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 875 | That said, this feature doesn't require additional syntax, and is |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 876 | likely to significantly reduce the stutter of repeated type arguments |
| 877 | in code.) |
| 878 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 879 | (Note: We could also consider supporting type inference for |
| 880 | composite literals of parameterized types. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 881 | |
| 882 | ```Go |
| 883 | type Pair(type T) struct { f1, f2 T } |
| 884 | var V = Pair{1, 2} // inferred as Pair(int){1, 2} |
| 885 | ``` |
| 886 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 887 | It's not clear how often this will arise in real code.) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 888 | |
| 889 | ### Instantiating a function |
| 890 | |
| 891 | Go normally permits you to refer to a function without passing any |
| 892 | arguments, producing a value of function type. |
| 893 | You may not do this with a function that has type parameters; all type |
| 894 | arguments must be known at compile time. |
| 895 | However, you can instantiate the function, by passing type arguments, |
| 896 | without passing any non-type arguments. |
| 897 | This will produce an ordinary function value with no type parameters. |
| 898 | |
| 899 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 900 | // PrintInts will be type func([]int). |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 901 | var PrintInts = Print(int) |
| 902 | ``` |
| 903 | |
| 904 | ### Type assertions and switches |
| 905 | |
| 906 | A useful function with type parameters will support any type argument |
| 907 | that implements the contract. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 908 | Sometimes, though, it's possible to use a more efficient |
| 909 | function implementation for some type arguments. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 910 | The language already has mechanisms for code to find out what type it |
| 911 | is working with: type assertions and type switches. |
| 912 | Those are normally only permitted with interface types. |
| 913 | In this design, functions are also permitted to use them with values |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 914 | whose types are type parameters, or are based on type parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 915 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 916 | This doesn't add any functionality, as the function could get the same |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 917 | information using the reflect package. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 918 | It's merely occasionally convenient, and it may result in more |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 919 | efficient code. |
| 920 | |
| 921 | For example, this code is permitted even if it is called with a type |
| 922 | argument that is not an interface type. |
| 923 | |
| 924 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 925 | contract reader(T) { |
| 926 | T Read([]byte) (int, error) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 927 | } |
| 928 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 929 | func ReadByte(type T reader)(r T) (byte, error) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 930 | if br, ok := r.(io.ByteReader); ok { |
| 931 | return br.ReadByte() |
| 932 | } |
| 933 | var b [1]byte |
| 934 | _, err := r.Read(b[:]) |
| 935 | return b[0], err |
| 936 | } |
| 937 | ``` |
| 938 | |
| 939 | ### Instantiating types in type literals |
| 940 | |
| 941 | When instantiating a type at the end of a type literal, there is a |
| 942 | parsing ambiguity. |
| 943 | |
| 944 | ```Go |
| 945 | x1 := []T(v1) |
| 946 | x2 := []T(v2){} |
| 947 | ``` |
| 948 | |
| 949 | In this example, the first case is a type conversion of `v1` to the |
| 950 | type `[]T`. |
| 951 | The second case is a composite literal of type `[]T(v2)`, where `T` is |
| 952 | a parameterized type that we are instantiating with the type argument |
| 953 | `v2`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 954 | The ambiguity is at the point where we see the open parenthesis: at |
| 955 | that point the parser doesn't know whether it is seeing a type |
| 956 | conversion or something like a composite literal. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 957 | |
| 958 | To avoid this ambiguity, we require that type instantiations at the |
| 959 | end of a type literal be parenthesized. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 960 | To write a type literal that is a slice of a type instantiation, you |
| 961 | must write `[](T(v1))`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 962 | Without those parentheses, `[]T(x)` is parsed as `([]T)(x)`, not as |
| 963 | `[](T(x))`. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 964 | This only applies to slice, array, map, chan, and func type literals |
| 965 | ending in a type name. |
| 966 | Of course it is always possible to use a separate type declaration to |
| 967 | give a name to the instantiated type, and to use that. |
| 968 | This is only an issue when the type is instantiated in place. |
| 969 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 970 | ### Using parameterized types as unnamed function parameter types |
| 971 | |
| 972 | When parsing a parameterized type as an unnamed function parameter |
| 973 | type, there is a parsing ambiguity. |
| 974 | |
| 975 | ```Go |
| 976 | var f func(x(T)) |
| 977 | ``` |
| 978 | |
| 979 | In this example we don't know whether the function has a single |
| 980 | unnamed parameter of the parameterized type `x(T)`, or whether this is |
| 981 | a named parameter `x` of the type `(T)` (written with parentheses). |
| 982 | |
| 983 | For backward compatibility, we treat this as the latter case: `x(T)` |
| 984 | is a parameter `x` of type `(T)`. |
| 985 | In order to describe a function with a single unnamed parameter of |
| 986 | type `x(T)`, either the parameter must be named, or extra parentheses |
| 987 | must be used. |
| 988 | |
| 989 | ```Go |
| 990 | var f1 func(_ x(T)) |
| 991 | var f2 func((x(T))) |
| 992 | ``` |
| 993 | |
Robert Griesemer | 020fa4d | 2020-02-26 13:17:55 -0800 | [diff] [blame] | 994 | ### Embedding a parameterized type in a struct |
| 995 | |
| 996 | There is a parsing ambiguity when embedding a parameterized type |
| 997 | in a struct type. |
| 998 | |
| 999 | ```Go |
| 1000 | type S1(type T) struct { |
| 1001 | f T |
| 1002 | } |
| 1003 | |
| 1004 | type S2 struct { |
| 1005 | S1(int) |
| 1006 | } |
| 1007 | ``` |
| 1008 | |
| 1009 | In this example we don't know whether struct `S2` has a single |
| 1010 | field named `S1` of type `(int)`, or whether we |
| 1011 | are trying to embed the instantiated type `S1(int)` into `S2`. |
| 1012 | |
| 1013 | For backward compatibility, we treat this as the former case: `S2` has |
| 1014 | a field named `S1`. |
| 1015 | |
| 1016 | In order to embed an instantiated type in a struct, we could require that |
| 1017 | extra parentheses be used. |
| 1018 | |
| 1019 | ```Go |
| 1020 | type S2 struct { |
| 1021 | (S1(int)) |
| 1022 | } |
| 1023 | ``` |
| 1024 | |
| 1025 | This is currently not supported by the language, so this would suggest |
| 1026 | generally extending the language to permit types embedded in structs to |
| 1027 | be parenthesized. |
| 1028 | |
| 1029 | ### Embedding a parameterized interface type in an interface |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1030 | |
| 1031 | There is a parsing ambiguity when embedding a parameterized interface |
| 1032 | type in another interface type. |
| 1033 | |
| 1034 | ```Go |
| 1035 | type I1(type T) interface { |
| 1036 | M(T) |
| 1037 | } |
| 1038 | |
| 1039 | type I2 interface { |
| 1040 | I1(int) |
| 1041 | } |
| 1042 | ``` |
| 1043 | |
| 1044 | In this example we don't know whether interface `I2` has a single |
| 1045 | method named `I1` that takes an argument of type `int`, or whether we |
| 1046 | are trying to embed the instantiated type `I1(int)` into `I2`. |
| 1047 | |
| 1048 | For backward compatibility, we treat this as the former case: `I2` has |
| 1049 | a method named `I1`. |
| 1050 | |
| 1051 | In order to embed an instantiated interface, we could require that |
| 1052 | extra parentheses be used. |
| 1053 | |
| 1054 | ```Go |
| 1055 | type I2 interface { |
| 1056 | (I1(int)) |
| 1057 | } |
| 1058 | ``` |
| 1059 | |
| 1060 | This is currently not supported by the language, so this would suggest |
| 1061 | generally extending the language to permit embedded interface types to |
| 1062 | be parenthesized. |
| 1063 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1064 | ### Reflection |
| 1065 | |
| 1066 | We do not propose to change the reflect package in any way. |
| 1067 | When a type or function is instantiated, all of the type parameters |
| 1068 | will become ordinary non-generic types. |
| 1069 | The `String` method of a `reflect.Type` value of an instantiated type |
| 1070 | will return the name with the type arguments in parentheses. |
| 1071 | For example, `List(int)`. |
| 1072 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1073 | It's impossible for non-generic code to refer to generic code without |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1074 | instantiating it, so there is no reflection information for |
| 1075 | uninstantiated generic types or functions. |
| 1076 | |
| 1077 | ### Contracts details |
| 1078 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1079 | Let's take a deeper look at contracts. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1080 | |
| 1081 | Operations on values whose type is a type parameter must be permitted |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1082 | by the type parameter's contract. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1083 | This means that the power of generic functions is tied precisely to |
| 1084 | the interpretation of the contract body. |
| 1085 | It also means that the language requires a precise definition of the |
| 1086 | operations that are permitted by a given contract. |
| 1087 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1088 | #### Methods |
| 1089 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1090 | All the contracts we've seen so far show only method calls in the |
| 1091 | contract body. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1092 | If a method call appears in the contract body, that method may be |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1093 | called on a value in any statement or expression in the function |
| 1094 | body. |
| 1095 | It will take argument and result types as specified in the contract |
| 1096 | body. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1097 | |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1098 | #### Pointer methods |
| 1099 | |
| 1100 | In some cases we need to require that a method be a pointer method. |
| 1101 | This will happen when a function needs to declare variables whose |
| 1102 | type is the type parameter, and also needs to call methods that are |
| 1103 | defined for the pointer to the type parameter. |
| 1104 | |
| 1105 | For example: |
| 1106 | |
| 1107 | ```Go |
| 1108 | contract setter(T) { |
| 1109 | T Set(string) |
| 1110 | } |
| 1111 | |
| 1112 | func Init(type T setter)(s string) T { |
| 1113 | var r T |
| 1114 | r.Set(s) |
| 1115 | return r |
| 1116 | } |
| 1117 | |
| 1118 | type MyInt int |
| 1119 | |
| 1120 | func (p *MyInt) Set(s string) { |
| 1121 | v, err := strconv.Atoi(s) |
| 1122 | if err != nil { |
| 1123 | log.Fatal("Init failed", err) |
| 1124 | } |
| 1125 | *p = MyInt(v) |
| 1126 | } |
| 1127 | |
| 1128 | // INVALID |
| 1129 | // MyInt does not have a Set method, only *MyInt has one. |
| 1130 | var Init1 = Init(MyInt)("1") |
| 1131 | |
| 1132 | // DOES NOT WORK |
| 1133 | // r in Init is type *MyInt with value nil, |
| 1134 | // so the Set method does a nil pointer deference. |
| 1135 | var Init2 = Init(*MyInt)("2") |
| 1136 | ``` |
| 1137 | |
| 1138 | The function `Init` cannot be instantiated with the type `MyInt`, as |
| 1139 | that type does not have a method `Set`; only `*MyInt` has `Set`. |
| 1140 | |
| 1141 | But instantiating `Init` with `*MyInt` doesn't work either, as then |
| 1142 | the local variable `r` in `Init` is a value of type `*MyInt` |
| 1143 | initialized to the zero value, which for a pointer is `nil`. |
| 1144 | The `Init` function then invokes the `Set` method on a `nil` pointer, |
| 1145 | causing a `nil` pointer dereference at the line `*p = MyInt(v)`. |
| 1146 | |
| 1147 | In order to permit this kind of code, contracts permit specifying that |
| 1148 | for a type parameter `T` the pointer type `*T` has a method. |
| 1149 | |
| 1150 | ```Go |
| 1151 | contract setter(T) { |
| 1152 | *T Set(string) |
| 1153 | } |
| 1154 | ``` |
| 1155 | |
| 1156 | With this definition of `setter`, instantiating `Init` with `MyInt` is |
| 1157 | valid and the code works. |
| 1158 | The local variable `r` has type `MyInt`, and the address of `r` is |
| 1159 | passed as the receiver of the `Set` pointer method. |
| 1160 | Instantiating `Init` with `*MyInt` is now invalid, as the type |
| 1161 | `**MyInt` does not have a method `Set`. |
| 1162 | |
| 1163 | Listing a `*T` method in a contract means that the method must be on |
| 1164 | the type `*T`, and it means that the parameterized function is only |
| 1165 | permitted to call the method on an addressable value of type `T`. |
| 1166 | |
| 1167 | #### Pointer or value methods |
| 1168 | |
| 1169 | If a method is listed in a contract with a plain `T` rather than `*T`, |
| 1170 | then it may be either a pointer method or a value method of `T`. |
| 1171 | In order to avoid worrying about this distinction, in a generic |
| 1172 | function body all method calls will be pointer method calls. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1173 | If necessary, the function body will insert temporary variables, |
| 1174 | not seen by the user, in order to get an addressable variable to use |
| 1175 | to call the method. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1176 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1177 | For example, this code is valid, even though `LookupAsString` calls |
| 1178 | `String` in a context that requires a value method, and `MyInt` only |
| 1179 | has a pointer method. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1180 | |
| 1181 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1182 | func LookupAsString(type T stringer)(m map[int]T, k int) string { |
| 1183 | return m[k].String() // Note: calls method on value of type T |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1184 | } |
| 1185 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1186 | type MyInt int |
| 1187 | func (p *MyInt) String() { return strconv.Itoa(int(*p)) } |
| 1188 | func F(m map[int]MyInt) string { |
| 1189 | return LookupAsString(MyInt)(m, 0) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1190 | } |
| 1191 | ``` |
| 1192 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1193 | This makes it easier to understand which types satisfy a contract, and |
| 1194 | how a contract may be used. |
| 1195 | It has the drawback that in some cases a pointer method that modifies |
| 1196 | the value to which the receiver points may be called on a temporary |
| 1197 | variable that is discarded after the method completes. |
| 1198 | It may be possible to add a vet warning for a case where a generic |
| 1199 | function uses a temporary variable for a method call and the function |
| 1200 | is instantiated with a type that has only a pointer method, not a |
| 1201 | value method. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1202 | |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1203 | (Note: we should revisit this decision if it leads to confusion or |
| 1204 | incorrect code.) |
| 1205 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1206 | #### Operators |
| 1207 | |
| 1208 | Method calls are not sufficient for everything we want to express. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1209 | Consider this simple function that returns the smallest element of a |
| 1210 | slice of values, where the slice is assumed to be non-empty. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1211 | |
| 1212 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1213 | // This function is INVALID. |
| 1214 | func Smallest(type T)(s []T) T { |
| 1215 | r := s[0] // panics if slice is empty |
| 1216 | for _, v := range s[1:] { |
| 1217 | if v < r { // INVALID |
| 1218 | r = v |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1219 | } |
| 1220 | } |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1221 | return r |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1222 | } |
| 1223 | ``` |
| 1224 | |
| 1225 | Any reasonable generics implementation should let you write this |
| 1226 | function. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1227 | The problem is the expression `v < r`. |
| 1228 | This assumes that `T` supports the `<` operator, but there is no |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1229 | contract requiring that. |
| 1230 | Without a contract the function body can only use operations that are |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1231 | available for all types, but not all Go types support `<`. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1232 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1233 | It follows that we need a way to write a contract that accepts only |
| 1234 | types that support `<`. |
| 1235 | In order to do that, we observe that, aside from two exceptions that |
| 1236 | we will discuss later, all the arithmetic, comparison, and logical |
| 1237 | operators defined by the language may only be used with types that are |
| 1238 | predeclared by the language, or with defined types whose underlying |
| 1239 | type is one of those predeclared types. |
| 1240 | That is, the operator `<` can only be used with a predeclared type |
| 1241 | such as `int` or `float64`, or a defined type whose underlying type is |
| 1242 | one of those types. |
| 1243 | Go does not permit using `<` with an aggregate type or with an |
| 1244 | arbitrary defined type. |
| 1245 | |
| 1246 | This means that rather than try to write a contract for `<`, we can |
| 1247 | approach this the other way around: instead of saying which operators |
| 1248 | a contract should support, we can say which (underlying) types a |
| 1249 | contract should accept. |
| 1250 | |
| 1251 | #### Types in contracts |
| 1252 | |
| 1253 | A contract may list explicit types that may be used as type |
| 1254 | arguments. |
| 1255 | These are expressed in the form `type-parameter-name type, type...`. |
| 1256 | The `type` must be a predeclared type, such as `int`, or an aggregate |
| 1257 | as discussed below. |
| 1258 | For example, |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1259 | |
| 1260 | ```Go |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1261 | contract SignedInteger(T) { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1262 | T int, int8, int16, int32, int64 |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1263 | } |
| 1264 | ``` |
| 1265 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1266 | This contract specifies that the type argument must be one of the |
| 1267 | listed types (`int`, `int8`, and so forth), or it must be a defined |
| 1268 | type whose underlying type is one of the listed types. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1269 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1270 | When a parameterized function using this contract has a value of type |
| 1271 | `T`, it may use any operation that is permitted by all of the listed |
| 1272 | types. |
| 1273 | This can be an operation like `<`, or for aggregate types an operation |
| 1274 | like `range` or `<-`. |
| 1275 | If the function can be compiled successfully using each type listed in |
| 1276 | the contract, then the operation is permitted. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1277 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1278 | For the `Smallest` example shown earlier, we could use a contract like |
| 1279 | this: |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1280 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1281 | ```Go |
| 1282 | contract Ordered(T) { |
| 1283 | T int, int8, int16, int32, int64, |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1284 | uint, uint8, uint16, uint32, uint64, uintptr, |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1285 | float32, float64, |
| 1286 | string |
| 1287 | } |
| 1288 | ``` |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1289 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1290 | (In practice this contract would likely be defined and exported in a |
| 1291 | new standard library package, `contracts`, so that it could be used by |
| 1292 | function and type and contract definitions.) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1293 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1294 | Given that contract, we can write this function, now valid: |
| 1295 | |
| 1296 | ```Go |
| 1297 | func Smallest(type T Ordered)(s []T) T { |
| 1298 | r := s[0] // panics if slice is empty |
| 1299 | for _, v := range s[1:] { |
| 1300 | if v < r { |
| 1301 | r = v |
| 1302 | } |
| 1303 | } |
| 1304 | return r |
| 1305 | } |
| 1306 | ``` |
| 1307 | |
| 1308 | #### Conjunction and disjunction in contracts |
| 1309 | |
| 1310 | The use of comma to separate types is a general mechanism. |
| 1311 | A contract can be considered as a set of constraints, where the |
| 1312 | constraints are either methods or types. |
| 1313 | Separating constraints by a semicolon or newline means that the |
| 1314 | constraints are a conjunction: each constraint must be satisfied. |
| 1315 | Separating constraints by a comma means that the constraints are a |
| 1316 | disjunction: at least one of the constraints must be satisified. |
| 1317 | |
| 1318 | With a conjunction of constraints in a contract, a generic function |
| 1319 | may use any operation permitted by at least one of the constraints. |
| 1320 | With a disjunction, a generic function may use any operation permitted |
| 1321 | by all of the constraints. |
| 1322 | |
| 1323 | Syntactically, the type parameter being constrained must be listed for |
| 1324 | each individual conjunction constraint, but only once for the |
| 1325 | disjunction constraints. |
| 1326 | |
| 1327 | Normally methods will be listed as a conjunction, separated by a |
| 1328 | semicolon or newline. |
| 1329 | |
| 1330 | ```Go |
| 1331 | // PrintStringer1 and PrintStringer2 are equivalent. |
| 1332 | contract PrintStringer1(T) { |
| 1333 | T String() string |
| 1334 | T Print() |
| 1335 | } |
| 1336 | |
| 1337 | contract PrintStringer2(T) { |
| 1338 | T String() string; T Print() |
| 1339 | } |
| 1340 | ``` |
| 1341 | |
| 1342 | Normally builtin types will be listed as a disjunction, separated by |
| 1343 | commas. |
| 1344 | |
| 1345 | ```Go |
| 1346 | contract Float(T) { |
| 1347 | T float32, float64 |
| 1348 | } |
| 1349 | ``` |
| 1350 | |
| 1351 | However, this is not required. |
| 1352 | For example: |
| 1353 | |
| 1354 | ```Go |
| 1355 | contract IOCloser(S) { |
| 1356 | S Read([]byte) (int, error), // note trailing comma |
| 1357 | Write([]byte) (int, error) |
| 1358 | S Close() error |
| 1359 | } |
| 1360 | ``` |
| 1361 | |
| 1362 | This contract accepts any type that has a `Close` method and also has |
| 1363 | either a `Read` or a `Write` method (or both). |
| 1364 | To put it another way, it accepts any type that implements either |
| 1365 | `io.ReadCloser` or `io.WriteCloser` (or both). |
| 1366 | In a generic function using this contract permits calling the |
| 1367 | `Close` method, but calling the `Read` or `Write` method requires a |
| 1368 | type assertion to an interface type. |
| 1369 | It's not clear whether this is useful, but it is valid. |
| 1370 | |
| 1371 | Another, pedantic, example: |
| 1372 | |
| 1373 | ```Go |
| 1374 | contract unsatisfiable(T) { |
| 1375 | T int |
| 1376 | T uint |
| 1377 | } |
| 1378 | ``` |
| 1379 | |
| 1380 | This contract permits any type that is both `int` and `uint`. |
| 1381 | Since there is no such type, the contract does not permit any type. |
| 1382 | This is valid but useless. |
| 1383 | |
| 1384 | #### Both types and methods in contracts |
| 1385 | |
| 1386 | A contract may list both builtin types and methods, typically using |
| 1387 | conjunctions and disjunctions as follows: |
| 1388 | |
| 1389 | ```Go |
| 1390 | contract StringableSignedInteger(T) { |
| 1391 | T int, int8, int16, int32, int64 |
| 1392 | T String() string |
| 1393 | } |
| 1394 | ``` |
| 1395 | |
| 1396 | This contract permits any type defined as one of the listed types, |
| 1397 | provided it also has a `String() string` method. |
| 1398 | Although the `StringableSignedInteger` contract explicitly lists |
| 1399 | `int`, the type `int` is not permitted as a type argument, since `int` |
| 1400 | does not have a `String` method. |
| 1401 | An example of a type argument that would be permitted is `MyInt`, |
| 1402 | defined as: |
| 1403 | |
| 1404 | ```Go |
| 1405 | type MyInt int |
| 1406 | func (mi MyInt) String() string { |
| 1407 | return fmt.Sprintf("MyInt(%d)", mi) |
| 1408 | } |
| 1409 | ``` |
| 1410 | |
| 1411 | #### Aggregate types in contracts |
| 1412 | |
| 1413 | A type in a contract need not be a predeclared type; it can be a type |
| 1414 | literal composed of predeclared types. |
| 1415 | |
| 1416 | ```Go |
| 1417 | contract byteseq(T) { |
| 1418 | T string, []byte |
| 1419 | } |
| 1420 | ``` |
| 1421 | |
| 1422 | The same rules apply. |
| 1423 | The type argument for this contract may be `string` or `[]byte` or a |
| 1424 | type whose underlying type is one of those. |
| 1425 | A parameterized function with this contract may use any operation |
| 1426 | permitted by both `string` and `[]byte`. |
| 1427 | |
| 1428 | Given these definitions |
| 1429 | |
| 1430 | ```Go |
| 1431 | type MyByte byte |
| 1432 | type MyByteAlias = byte |
| 1433 | ``` |
| 1434 | |
| 1435 | the `byteseq` contract is satisfied by any of `string`, `[]byte`, |
| 1436 | `[]MyByte`, `[]MyByteAlias`. |
| 1437 | |
| 1438 | The `byteseq` contract permits writing generic functions that work |
| 1439 | for either `string` or `[]byte` types. |
| 1440 | |
| 1441 | ```Go |
| 1442 | func Join(type T byteseq)(a []T, sep T) (ret T) { |
| 1443 | if len(a) == 0 { |
| 1444 | // Use the result parameter as a zero value; |
| 1445 | // see discussion of zero value below. |
| 1446 | return ret |
| 1447 | } |
| 1448 | if len(a) == 1 { |
| 1449 | return T(append([]byte(nil), a[0]...)) |
| 1450 | } |
| 1451 | n := len(sep) * (len(a) - 1) |
| 1452 | for i := 0; i < len(a); i++ { |
| 1453 | n += len(a[i]) // len works for both string and []byte |
| 1454 | } |
Andrew Bonventre | d41bf0f | 2019-07-29 16:25:31 -0400 | [diff] [blame] | 1455 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1456 | b := make([]byte, n) |
| 1457 | bp := copy(b, a[0]) |
| 1458 | for _, s := range a[1:] { |
| 1459 | bp += copy(b[bp:], sep) |
| 1460 | bp += copy(b[bp:], s) |
| 1461 | } |
| 1462 | return T(b) |
| 1463 | } |
| 1464 | ``` |
| 1465 | |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1466 | #### Aggregates of type parameters in contracts |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1467 | |
| 1468 | A type literal in a contract can refer not only to predeclared types, |
| 1469 | but also to type parameters. |
| 1470 | In this example, the `Slice` contract takes two parameters. |
| 1471 | The first type parameter is required to be a slice of the second. |
| 1472 | There are no constraints on the second type parameter. |
| 1473 | |
| 1474 | ```Go |
| 1475 | contract Slice(S, Element) { |
| 1476 | S []Element |
| 1477 | } |
| 1478 | ``` |
| 1479 | |
| 1480 | We can use the `Slice` contract to define a function that takes an |
| 1481 | argument of a slice type and returns a result of that same type. |
| 1482 | |
| 1483 | ```Go |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1484 | func Map(type S, Element Slice)(s S, f func(Element) Element) S { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1485 | r := make(S, len(s)) |
| 1486 | for i, v := range s { |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1487 | r[i] = f(v) |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1488 | } |
| 1489 | return r |
| 1490 | } |
| 1491 | |
| 1492 | type MySlice []int |
| 1493 | |
| 1494 | func DoubleMySlice(s MySlice) MySlice { |
| 1495 | v := Map(MySlice, int)(s, func(e int) int { return 2 * e }) |
| 1496 | // Here v has type MySlice, not type []int. |
| 1497 | return v |
| 1498 | } |
| 1499 | ``` |
| 1500 | |
| 1501 | (Note: the type inference rules described above do not permit |
| 1502 | inferring both `MySlice` and `int` when `DoubleMySlice` calls `Map`. |
| 1503 | It may be worth extending them, to make it easier to use functions |
| 1504 | that are careful to return the same result type as input type. |
| 1505 | Similarly, we would consider extending the type inference rules to |
| 1506 | permit inferring the type `Edge` from the type `Node` in the |
| 1507 | `graph.New` example shown earlier.) |
| 1508 | |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 1509 | To avoid a parsing ambiguity, when a type literal in a contract refers |
| 1510 | to a parameterized type, extra parentheses are required, so that it is |
| 1511 | not confused with a method. |
| 1512 | |
| 1513 | ```Go |
| 1514 | type M(type T) []T |
| 1515 | |
| 1516 | contract C(T) { |
| 1517 | T M(T) // T must implement the method M with an argument of type T |
| 1518 | T (M(T)) // T must be the type M(T) |
| 1519 | } |
| 1520 | ``` |
| 1521 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1522 | #### Comparable types in contracts |
| 1523 | |
| 1524 | Earlier we mentioned that there are two exceptions to the rule that |
| 1525 | operators may only be used with types that are predeclared by the |
| 1526 | language. |
| 1527 | The exceptions are `==` and `!=`, which are permitted for struct, |
| 1528 | array, and interface types. |
| 1529 | These are useful enough that we want to be able to write a contract |
| 1530 | that accepts any comparable type. |
| 1531 | |
| 1532 | To do this we introduce a new predeclared contract: `comparable`. |
| 1533 | The `comparable` contract takes a single type parameter. |
| 1534 | It accepts as a type argument any comparable type. |
| 1535 | It permits in a parameterized function the use of `==` and `!=` with |
| 1536 | values of that type parameter. |
| 1537 | |
| 1538 | As a predeclared contract, `comparable` may be used in a function or |
| 1539 | type definition, or it may be embedded in another contract. |
| 1540 | |
| 1541 | For example, this function may be instantiated with any comparable |
| 1542 | type: |
| 1543 | |
| 1544 | ```Go |
| 1545 | func Index(type T comparable)(s []T, x T) int { |
| 1546 | for i, v := range s { |
| 1547 | if v == x { |
| 1548 | return i |
| 1549 | } |
| 1550 | } |
| 1551 | return -1 |
| 1552 | } |
| 1553 | ``` |
| 1554 | |
| 1555 | #### Observations on types in contracts |
| 1556 | |
| 1557 | It may seem awkward to explicitly list types in a contract, but it is |
| 1558 | clear both as to which type arguments are permitted at the call site, |
| 1559 | and which operations are permitted by the parameterized function. |
| 1560 | |
| 1561 | If the language later changes to support operator methods (there are |
| 1562 | no such plans at present), then contracts will handle them as they do |
| 1563 | any other kind of method. |
| 1564 | |
| 1565 | There will always be a limited number of predeclared types, and a |
| 1566 | limited number of operators that those types support. |
| 1567 | Future language changes will not fundamentally change those facts, so |
| 1568 | this approach will continue to be useful. |
| 1569 | |
| 1570 | This approach does not attempt to handle every possible operator. |
| 1571 | For example, there is no way to usefully express the struct field |
| 1572 | reference `.` or the general index operator `[]`. |
| 1573 | The expectation is that those will be handled using aggregate types in |
| 1574 | a parameterized function definition, rather than requiring aggregate |
| 1575 | types as a type argument. |
| 1576 | For example, we expect functions that want to index into a slice to be |
| 1577 | parameterized on the slice element type `T`, and to use parameters or |
| 1578 | variables of type `[]T`. |
| 1579 | |
| 1580 | As shown in the `DoubleMySlice` example above, this approach makes it |
| 1581 | awkward to write generic functions that accept and return an aggregate |
| 1582 | type and want to return the same result type as their argument type. |
| 1583 | Defined aggregate types are not common, but they do arise. |
| 1584 | This awkwardness is a weakness of this approach. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1585 | |
| 1586 | #### Type conversions |
| 1587 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1588 | In a function with two type parameters `From` and `To`, a value of |
| 1589 | type `From` may be converted to a value of type `To` if all the |
| 1590 | types accepted by `From`'s contract can be converted to all the |
| 1591 | types accepted by `To`'s contract. |
| 1592 | If either type parameter does not accept types, then type conversions |
| 1593 | are not permitted. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1594 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1595 | For example: |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1596 | |
| 1597 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1598 | contract integer(T) { |
| 1599 | T int, int8, int16, int32, int64, |
| 1600 | uint, uint8, uint16, uint32, uint64, uintptr |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1601 | } |
| 1602 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1603 | contract integer2(T1, T2) { |
| 1604 | integer(T1) |
| 1605 | integer(T2) |
| 1606 | } |
Andrew Bonventre | d41bf0f | 2019-07-29 16:25:31 -0400 | [diff] [blame] | 1607 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1608 | func Convert(type To, From integer2)(from From) To { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1609 | to := To(from) |
| 1610 | if From(to) != from { |
| 1611 | panic("conversion out of range") |
| 1612 | } |
| 1613 | return to |
| 1614 | } |
| 1615 | ``` |
| 1616 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1617 | The type conversions in `Convert` are permitted because Go permits |
| 1618 | every integer type to be converted to every other integer type. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1619 | |
| 1620 | #### Untyped constants |
| 1621 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1622 | Some functions use untyped constants. |
| 1623 | An untyped constant is permitted with a value of some type parameter |
| 1624 | if it is permitted with every type accepted by the type parameter's |
| 1625 | contract. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1626 | |
| 1627 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1628 | contract integer(T) { |
| 1629 | T int, int8, int16, int32, int64, |
| 1630 | uint, uint8, uint16, uint32, uint64, uintptr |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1631 | } |
| 1632 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1633 | func Add10(type T integer)(s []T) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1634 | for i, v := range s { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1635 | s[i] = v + 10 // OK: 10 can convert to any integer type |
| 1636 | } |
| 1637 | } |
| 1638 | |
| 1639 | // This function is INVALID. |
| 1640 | func Add1024(type T integer)(s []T) { |
| 1641 | for i, v := range s { |
| 1642 | s[i] = v + 1024 // INVALID: 1024 not permitted by int8/uint8 |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1643 | } |
| 1644 | } |
| 1645 | ``` |
| 1646 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1647 | ### Implementation |
| 1648 | |
| 1649 | Russ Cox [famously observed](https://research.swtch.com/generic) that |
| 1650 | generics require choosing among slow programmers, slow compilers, or |
| 1651 | slow execution times. |
| 1652 | |
| 1653 | We believe that this design permits different implementation choices. |
| 1654 | Code may be compiled separately for each set of type arguments, or it |
| 1655 | may be compiled as though each type argument is handled similarly to |
| 1656 | an interface type with method calls, or there may be some combination |
| 1657 | of the two. |
| 1658 | |
| 1659 | In other words, this design permits people to stop choosing slow |
| 1660 | programmers, and permits the implementation to decide between slow |
| 1661 | compilers (compile each set of type arguments separately) or slow |
| 1662 | execution times (use method calls for each operation on a value of a |
| 1663 | type argument). |
| 1664 | |
| 1665 | ### Summary |
| 1666 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1667 | While this document is long and detailed, the actual design reduces to |
| 1668 | a few major points. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1669 | |
| 1670 | * Functions and types can have type parameters, which are defined |
| 1671 | using optional contracts. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1672 | * Contracts describe the methods required and the builtin types |
| 1673 | permitted for a type argument. |
| 1674 | * Contracts describe the methods and operations permitted for a type |
| 1675 | parameter. |
| 1676 | * Type inference will often permit omitting type arguments when |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1677 | calling functions with type parameters. |
| 1678 | |
| 1679 | This design is completely backward compatible, in that any valid Go 1 |
| 1680 | program will still be valid if this design is adopted (assuming |
| 1681 | `contract` is treated as a pseudo-keyword that is only meaningful at |
| 1682 | top level). |
| 1683 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1684 | We believe that this design addresses people's needs for generic |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1685 | programming in Go, without making the language any more complex than |
| 1686 | necessary. |
| 1687 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1688 | We can't truly know the impact on the language without years of |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1689 | experience with this design. |
| 1690 | That said, here are some speculations. |
| 1691 | |
| 1692 | #### Complexity |
| 1693 | |
| 1694 | One of the great aspects of Go is its simplicity. |
| 1695 | Clearly this design makes the language more complex. |
| 1696 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1697 | We believe that the increased complexity is small for people reading |
| 1698 | well written generic code, rather than writing it. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1699 | Naturally people must learn the new syntax for declaring type |
| 1700 | parameters. |
| 1701 | The code within a generic function reads like ordinary Go code, as can |
| 1702 | be seen in the examples below. |
| 1703 | It is an easy shift to go from `[]int` to `[]T`. |
| 1704 | Type parameter contracts serve effectively as documentation, |
| 1705 | describing the type. |
| 1706 | |
| 1707 | We expect that most people will not write generic code themselves, but |
| 1708 | many people are likely to write packages that use generic code written |
| 1709 | by others. |
| 1710 | In the common case, generic functions work exactly like non-generic |
| 1711 | functions: you simply call them. |
| 1712 | Type inference means that you do not have to write out the type |
| 1713 | arguments explicitly. |
| 1714 | The type inference rules are designed to be unsurprising: either the |
| 1715 | type arguments are deduced correctly, or the call fails and requires |
| 1716 | explicit type parameters. |
| 1717 | Type inference uses type identity, with no attempt to resolve two |
| 1718 | types that are similar but not identical, which removes significant |
| 1719 | complexity. |
| 1720 | |
| 1721 | People using generic types will have to pass explicit type arguments. |
| 1722 | The syntax for this is familiar. |
| 1723 | The only change is passing arguments to types rather than only to |
| 1724 | functions. |
| 1725 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1726 | In general, we have tried to avoid surprises in the design. |
| 1727 | Only time will tell whether we succeeded. |
| 1728 | |
| 1729 | #### Pervasiveness |
| 1730 | |
| 1731 | We expect that a few new packages will be added to the standard |
| 1732 | library. |
| 1733 | A new `slices` packages will be similar to the existing bytes and |
| 1734 | strings packages, operating on slices of any element type. |
| 1735 | New `maps` and `chans` packages will provide simple algorithms that |
| 1736 | are currently duplicated for each element type. |
| 1737 | A `set` package may be added. |
| 1738 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1739 | A new `contracts` packages will provide standard embeddable contracts, |
| 1740 | such as contracts that permit all integer types or all numeric types. |
| 1741 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1742 | Packages like `container/list` and `container/ring`, and types like |
| 1743 | `sync.Map`, will be updated to be compile-time type-safe. |
| 1744 | |
| 1745 | The `math` package will be extended to provide a set of simple |
| 1746 | standard algorithms for all numeric types, such as the ever popular |
| 1747 | `Min` and `Max` functions. |
| 1748 | |
| 1749 | It is likely that new special purpose compile-time type-safe container |
| 1750 | types will be developed, and some may become widely used. |
| 1751 | |
| 1752 | We do not expect approaches like the C++ STL iterator types to become |
| 1753 | widely used. |
| 1754 | In Go that sort of idea is more naturally expressed using an interface |
| 1755 | type. |
| 1756 | In C++ terms, using an interface type for an iterator can be seen as |
| 1757 | carrying an abstraction penalty, in that run-time efficiency will be |
| 1758 | less than C++ approaches that in effect inline all code; we believe |
| 1759 | that Go programmers will continue to find that sort of penalty to be |
| 1760 | acceptable. |
| 1761 | |
| 1762 | As we get more container types, we may develop a standard `Iterator` |
| 1763 | interface. |
| 1764 | That may in turn lead to pressure to modify the language to add some |
| 1765 | mechanism for using an `Iterator` with the `range` clause. |
| 1766 | That is very speculative, though. |
| 1767 | |
| 1768 | #### Efficiency |
| 1769 | |
| 1770 | It is not clear what sort of efficiency people expect from generic |
| 1771 | code. |
| 1772 | |
| 1773 | Generic functions, rather than generic types, can probably be compiled |
| 1774 | using an interface-based approach. |
| 1775 | That will optimize compile time, in that the package is only compiled |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1776 | once, but there will be some run time cost. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1777 | |
| 1778 | Generic types may most naturally be compiled multiple times for each |
| 1779 | set of type arguments. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1780 | This will clearly carry a compile time cost, but there shouldn't be |
| 1781 | any run time cost. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1782 | Compilers can also choose to implement generic types similarly to |
| 1783 | interface types, using special purpose methods to access each element |
| 1784 | that depends on a type parameter. |
| 1785 | |
| 1786 | Only experience will show what people expect in this area. |
| 1787 | |
| 1788 | #### Omissions |
| 1789 | |
| 1790 | We believe that this design covers the basic requirements for |
| 1791 | generic programming. |
| 1792 | However, there are a number of programming constructs that are not |
| 1793 | supported. |
| 1794 | |
| 1795 | * No specialization. |
| 1796 | There is no way to write multiple versions of a generic function |
| 1797 | that are designed to work with specific type arguments (other than |
| 1798 | using type assertions or type switches). |
| 1799 | * No metaprogramming. |
| 1800 | There is no way to write code that is executed at compile time to |
| 1801 | generate code to be executed at run time. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1802 | * No higher level abstraction. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1803 | There is no way to speak about a function with type arguments other |
| 1804 | than to call it or instantiate it. |
| 1805 | There is no way to speak about a parameterized type other than to |
| 1806 | instantiate it. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1807 | * No general type description. |
| 1808 | For operator support contracts use specific types, rather than |
| 1809 | describing the characteristics that a type must have. |
| 1810 | This is easy to understand but may be limiting at times. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1811 | * No covariance or contravariance. |
| 1812 | * No operator methods. |
| 1813 | You can write a generic container that is compile-time type-safe, |
| 1814 | but you can only access it with ordinary methods, not with syntax |
| 1815 | like `c[k]`. |
| 1816 | Similarly, there is no way to use `range` with a generic container |
| 1817 | type. |
| 1818 | * No currying. |
| 1819 | There is no way to specify only some of the type arguments, other |
| 1820 | than by using a type alias or a helper function. |
| 1821 | * No adaptors. |
| 1822 | There is no way for a contract to define adaptors that could be used |
| 1823 | to support type arguments that do not already satisfy the contract, |
| 1824 | such as, for example, defining an `==` operator in terms of an |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1825 | `Equal` method, or vice-versa. |
fREW Schmidt | b347d16 | 2019-07-29 21:19:28 +0000 | [diff] [blame] | 1826 | * No parameterization on non-type values such as constants. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1827 | This arises most obviously for arrays, where it might sometimes be |
| 1828 | convenient to write `type Matrix(type n int) [n][n]float64`. |
| 1829 | It might also sometimes be useful to specify significant values for |
| 1830 | a container type, such as a default value for elements. |
| 1831 | |
| 1832 | #### Issues |
| 1833 | |
| 1834 | There are some issues with this design that deserve a more detailed |
| 1835 | discussion. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1836 | We think these issues are relatively minor compared to the design as a |
| 1837 | whole, but they still deserve a complete hearing and discussion. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1838 | |
| 1839 | ##### The zero value |
| 1840 | |
| 1841 | This design has no simple expression for the zero value of a type |
| 1842 | parameter. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1843 | For example, consider this implementation of optional values that uses |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1844 | pointers: |
| 1845 | |
| 1846 | ```Go |
| 1847 | type Optional(type T) struct { |
| 1848 | p *T |
| 1849 | } |
| 1850 | |
| 1851 | func (o Optional(T)) Val() T { |
| 1852 | if o.p != nil { |
| 1853 | return *o.p |
| 1854 | } |
| 1855 | var zero T |
| 1856 | return zero |
| 1857 | } |
| 1858 | ``` |
| 1859 | |
| 1860 | In the case where `o.p == nil`, we want to return the zero value of |
| 1861 | `T`, but we have no way to write that. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1862 | It would be nice to be able to write `return nil`, but that wouldn't |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1863 | work if `T` is, say, `int`; in that case we would have to write |
| 1864 | `return 0`. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1865 | And, of course, there is no contract to support either `return nil` or |
| 1866 | `return 0`. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1867 | |
| 1868 | Some approaches to this are: |
| 1869 | |
| 1870 | * Use `var zero T`, as above, which works with the existing design |
| 1871 | but requires an extra statement. |
| 1872 | * Use `*new(T)`, which is ugly but works with the existing design. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1873 | * For results only, name the result parameter `_`, and use a naked |
| 1874 | `return` statement to return the zero value. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1875 | * Extend the design to permit using `nil` as the zero value of any |
| 1876 | generic type (but see [issue 22729](https://golang.org/issue/22729)). |
| 1877 | * Extend the design to permit using `T{}`, where `T` is a type |
| 1878 | parameter, to indicate the zero value of the type. |
| 1879 | * Change the language to permit using `_` on the right hand of an |
| 1880 | assignment (including `return` or a function call) as proposed in |
| 1881 | [issue 19642](https://golang.org/issue/19642). |
| 1882 | |
| 1883 | We feel that more experience with this design is needed before |
| 1884 | deciding what, if anything, to do here. |
| 1885 | |
| 1886 | ##### Lots of irritating silly parentheses |
| 1887 | |
| 1888 | Calling a function with type parameters requires an additional list of |
| 1889 | type arguments if the type arguments can not be inferred. |
| 1890 | If the function returns a function, and we call that, we get still |
| 1891 | more parentheses. |
| 1892 | |
| 1893 | ```Go |
| 1894 | F(int, float64)(x, y)(s) |
| 1895 | ``` |
| 1896 | |
| 1897 | We experimented with other syntaxes, such as using a colon to separate |
| 1898 | the type arguments from the regular arguments. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1899 | The current design seems to be the nicest, but perhaps something |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1900 | better is possible. |
| 1901 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1902 | ##### Pointer vs. value methods in contracts |
| 1903 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1904 | Contracts do not provide a way to distinguish between pointer and |
| 1905 | value methods, so types that provide either will satisfy a contract. |
| 1906 | This in turn requires that parameterized functions always permit |
| 1907 | either kind of method. |
| 1908 | This may be confusing, in that a parameterized function may invoke a |
| 1909 | pointer method on a temporary value; if the pointer method changes the |
| 1910 | value to which the receiver points, those changes will be lost. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1911 | We will have to judge from experience how much this confuses people in |
| 1912 | practice. |
| 1913 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1914 | ##### Defined aggregate types |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1915 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1916 | As discussed above, an extra type parameter is required for a function |
| 1917 | to take, as an argument, a defined type whose underlying type is an |
| 1918 | aggregate type, and to return the same defined type as a result. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 1919 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 1920 | For example, this function will map a function across a slice. |
| 1921 | |
| 1922 | ```Go |
| 1923 | func Map(type Element)(s []Element, f func(Element) Element) []Element { |
| 1924 | r := make([]Element, len(s)) |
| 1925 | for i, v := range s { |
| 1926 | r[i] = f(v) |
| 1927 | } |
| 1928 | return r |
| 1929 | } |
| 1930 | ``` |
| 1931 | |
| 1932 | However, when called on a defined type, it will return a slice of the |
| 1933 | element type of that type, rather than the defined type itself. |
| 1934 | |
| 1935 | ```Go |
| 1936 | type MySlice []int |
| 1937 | |
| 1938 | func DoubleMySlice(s MySlice) MySlice { |
| 1939 | s2 := Map(s, func(e int) int { return 2 * e }) |
| 1940 | // Here s2 is type []int, not type MySlice. |
| 1941 | return MySlice(s2) |
| 1942 | } |
| 1943 | ``` |
| 1944 | |
| 1945 | As discussed above with an example, this can be avoided by using an |
| 1946 | extra type parameter for `Map`, and using a contract that describes |
| 1947 | the required relationship between the slice and element types. |
| 1948 | This works but is awkward. |
| 1949 | |
| 1950 | ##### Identifying the matched predeclared type |
| 1951 | |
| 1952 | In this design we suggest permitting type assertions and type switches |
| 1953 | on values whose types are based on type parameters, but those type |
| 1954 | assertions and switches would always test the actual type argument. |
| 1955 | The design doesn't provide any way to test the contract type matched |
| 1956 | by the type argument. |
| 1957 | |
| 1958 | Here is an example that shows the difference. |
| 1959 | |
| 1960 | ```Go |
| 1961 | contract Float(F) { |
| 1962 | F float32, float64 |
| 1963 | } |
| 1964 | |
| 1965 | func NewtonSqrt(type F Float)(v F) F { |
| 1966 | var iterations int |
| 1967 | switch v.(type) { |
| 1968 | case float32: |
| 1969 | iterations = 4 |
| 1970 | case float64: |
| 1971 | iterations = 5 |
| 1972 | default: |
| 1973 | panic(fmt.Sprintf("unexpected type %T", v)) |
| 1974 | } |
| 1975 | // Code omitted. |
| 1976 | } |
| 1977 | |
| 1978 | type MyFloat float32 |
| 1979 | |
| 1980 | var G = NewtonSqrt(MyFloat(64)) |
| 1981 | ``` |
| 1982 | |
| 1983 | This code will panic when initializing `G`, because the type of `v` in |
| 1984 | the `NewtonSqrt` function will be `MyFloat`, not `float32` or |
| 1985 | `float64`. |
| 1986 | What this function actually wants to test is not the type of `v`, but |
| 1987 | the type that `v` matched in the contract. |
| 1988 | |
| 1989 | One way to handle this would be to permit type switches on the type |
| 1990 | `F`, rather than the value `v`, with the proviso that the type `F` |
| 1991 | would always match a type defined in the contract. |
| 1992 | This kind of type switch would only be permitted if the contract does |
| 1993 | list explicit types, and only types listed in the contract would be |
| 1994 | permitted as cases. |
| 1995 | |
| 1996 | If we took this approach, we would stop permitting type assertions and |
| 1997 | switches on values whose type is based on a type parameter. |
| 1998 | Those assertions and switches can always be done by first converting |
| 1999 | the value to the empty interface type. |
| 2000 | |
| 2001 | A different approach would be that if a contract specifies any types |
| 2002 | for a type parameter, then let type switches and assertions on values |
| 2003 | whose type is, or is based on, that type parameter to match only the |
| 2004 | types listed in the type parameter's contract. |
| 2005 | It is still possible to match the value's actual type by first |
| 2006 | converting it to the `interface{}` type and then doing the type |
| 2007 | assertion or switch. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2008 | |
| 2009 | #### Discarded ideas |
| 2010 | |
| 2011 | This design is not perfect, and it will be changed as we gain |
| 2012 | experience with it. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2013 | That said, there are many ideas that we've already considered in |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2014 | detail. |
| 2015 | This section lists some of those ideas in the hopes that it will help |
| 2016 | to reduce repetitive discussion. |
| 2017 | The ideas are presented in the form of a FAQ. |
| 2018 | |
| 2019 | ##### Why not use interfaces instead of contracts? |
| 2020 | |
| 2021 | _The interface method syntax is familiar._ |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2022 | _Why introduce another way to write methods?_ |
| 2023 | |
| 2024 | Contracts, unlike interfaces, support multiple types, including |
| 2025 | describing ways that the types refer to each other. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2026 | |
| 2027 | It is unclear how to represent operators using interface methods. |
| 2028 | We considered syntaxes like `+(T, T) T`, but that is confusing and |
| 2029 | repetitive. |
| 2030 | Also, a minor point, but `==(T, T) bool` does not correspond to the |
| 2031 | `==` operator, which returns an untyped boolean value, not `bool`. |
| 2032 | We also considered writing simply `+` or `==`. |
| 2033 | That seems to work but unfortunately the semicolon insertion rules |
| 2034 | require writing a semicolon after each operator at the end of a line. |
| 2035 | Using contracts that look like functions gives us a familiar syntax at |
| 2036 | the cost of some repetition. |
| 2037 | These are not fatal problems, but they are difficulties. |
| 2038 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2039 | More seriously, a contract is a relationship between the definition of |
| 2040 | a generic function and the callers of that function. |
| 2041 | To put it another way, it is a relationship between a set of type |
| 2042 | parameters and a set of type arguments. |
| 2043 | The contract defines how values of the type parameters may be used, |
| 2044 | and defines the requirements on the type arguments. |
| 2045 | That is why it is called a contract: because it defines the behavior |
| 2046 | on both sides. |
| 2047 | |
| 2048 | An interface is a type, not a relationship between function |
| 2049 | definitions and callers. |
| 2050 | A program can have a value of an interface type, but it makes no sense |
| 2051 | to speak of a value of a contract type. |
| 2052 | A value of interface type has both a static type (the interface type) |
| 2053 | and a dynamic type (some non-interface type), but there is no similar |
| 2054 | concept for contracts. |
| 2055 | |
| 2056 | In other words, contracts are not extensions of interface types. |
| 2057 | There are things you can do with a contract that you cannot do with an |
| 2058 | interface type, and there are things you can do with an interace type |
| 2059 | that you cannot do with a contract. |
| 2060 | |
| 2061 | It is true that a contract that has a single type parameter and that |
| 2062 | lists only methods, not builtin types, for that type parameter, looks |
| 2063 | similar to an interface type. |
| 2064 | But all the similarity amounts to is that both provide a list of |
| 2065 | methods. |
| 2066 | We could consider permitting using an interface type as a contract |
| 2067 | with a single type parameter that lists only methods. |
| 2068 | But that should not mislead us into thinking that contracts are |
| 2069 | interfaces. |
| 2070 | |
| 2071 | ##### Why not permit contracts to describe a type? |
| 2072 | |
| 2073 | _In order to use operators contracts have to explicitly and tediously_ |
| 2074 | _list types._ |
| 2075 | _Why not permit them to describe a type?_ |
| 2076 | |
| 2077 | There are many different ways that a Go type can be used. |
| 2078 | While it is possible to invent notation to describe the various |
| 2079 | operations in a contract, it leads to a proliferation of additional |
| 2080 | syntactic constructs, making contracts complicated and hard to read. |
| 2081 | The approach used in this design is simpler and relies on only a few |
| 2082 | new syntactic constructs and names. |
| 2083 | |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2084 | ##### Why not put type parameters on packages? |
| 2085 | |
| 2086 | We investigated this extensively. |
| 2087 | It becomes problematic when you want to write a `list` package, and |
| 2088 | you want that package to include a `Transform` function that converts |
| 2089 | a `List` of one element type to a `List` of another element type. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2090 | It's very awkward for a function in one instantiation of a package to |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2091 | return a type that requires a different instantiation of the package. |
| 2092 | |
| 2093 | It also confuses package boundaries with type definitions. |
| 2094 | There is no particular reason to think that the uses of parameterized |
| 2095 | types will break down neatly into packages. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2096 | Sometimes they will, sometimes they won't. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2097 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2098 | ##### Why not use the syntax `F<T>` like C++ and Java? |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2099 | |
| 2100 | When parsing code within a function, such as `v := F<T>`, at the point |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2101 | of seeing the `<` it's ambiguous whether we are seeing a type |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2102 | instantiation or an expression using the `<` operator. |
| 2103 | Resolving that requires effectively unbounded lookahead. |
| 2104 | In general we strive to keep the Go parser simple. |
| 2105 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2106 | ##### Why not use the syntax `F[T]`? |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2107 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2108 | When parsing a type declaration `type A [T] int` it's ambiguous |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2109 | whether this is a parameterized type defined (uselessly) as `int` or |
| 2110 | whether it is an array type with `T` elements. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2111 | However, this would be addressed by requiring `type A [type T] int` |
| 2112 | for a parameterized type. |
| 2113 | |
| 2114 | Parsing declarations like `func f(A[T]int)` (a single parameter of |
| 2115 | type `[T]int`) and `func f(A[T], int)` (two parameters, one of type |
| 2116 | `A[T]` and one of type `int`) show that some additional parsing |
| 2117 | lookahead is required. |
| 2118 | This is solvable but adds parsing complexity. |
| 2119 | |
| 2120 | The language generally permits a trailing comma in a comma-separated |
| 2121 | list, so `A[T,]` should be permitted if `A` is a parameterized type, |
| 2122 | but normally would not be permitted for an index expression. |
| 2123 | However, the parser can't know whether `A` is a parameterized type or |
| 2124 | a value of slice, array, or map type, so this parse error can not be |
| 2125 | reported until after type checking is complete. |
| 2126 | Again, solvable but complicated. |
| 2127 | |
| 2128 | More generally, we felt that the square brackets were too intrusive on |
| 2129 | the page and that parentheses were more Go like. |
| 2130 | We will reevaluate this decision as we gain more experience. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2131 | |
| 2132 | ##### Why not use `F«T»`? |
| 2133 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2134 | We considered it but we couldn't bring ourselves to require |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2135 | non-ASCII. |
| 2136 | |
| 2137 | ##### Why not define contracts in a standard package? |
| 2138 | |
| 2139 | _Instead of writing out contracts, use names like_ |
| 2140 | _`contracts.Arithmetic` and `contracts.Comparable`._ |
| 2141 | |
| 2142 | Listing all the possible combinations of types gets rather lengthy. |
| 2143 | It also introduces a new set of names that not only the writer of |
| 2144 | generic code, but, more importantly, the reader, must remember. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2145 | One of the driving goals of this design is to introduce as few new |
| 2146 | names as possible. |
| 2147 | In this design we introduce one new keyword and one new predefined |
| 2148 | name. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2149 | |
| 2150 | We expect that if people find such names useful, we can introduce a |
| 2151 | package `contracts` that defines the useful names in the form of |
| 2152 | contracts that can be used by other types and functions and embedded |
| 2153 | in other contracts. |
| 2154 | |
| 2155 | #### Comparison with Java |
| 2156 | |
| 2157 | Most complaints about Java generics center around type erasure. |
| 2158 | This design does not have type erasure. |
| 2159 | The reflection information for a generic type will include the full |
| 2160 | compile-time type information. |
| 2161 | |
| 2162 | In Java type wildcards (`List<? extends Number>`, `List<? super |
| 2163 | Number>`) implement covariance and contravariance. |
| 2164 | These concepts are missing from Go, which makes generic types much |
| 2165 | simpler. |
| 2166 | |
| 2167 | #### Comparison with C++ |
| 2168 | |
| 2169 | C++ templates do not enforce any constraints on the type arguments |
| 2170 | (unless the concept proposal is adopted). |
| 2171 | This means that changing template code can accidentally break far-off |
| 2172 | instantiations. |
| 2173 | It also means that error messages are reported only at instantiation |
| 2174 | time, and can be deeply nested and difficult to understand. |
| 2175 | This design avoids these problems through explicit contracts. |
| 2176 | |
| 2177 | C++ supports template metaprogramming, which can be thought of as |
| 2178 | ordinary programming done at compile time using a syntax that is |
| 2179 | completely different than that of non-template C++. |
| 2180 | This design has no similar feature. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2181 | This saves considerable complexity while losing some power and run |
| 2182 | time efficiency. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2183 | |
| 2184 | ### Examples |
| 2185 | |
| 2186 | The following sections are examples of how this design could be used. |
| 2187 | This is intended to address specific areas where people have created |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2188 | user experience reports concerned with Go's lack of generics. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2189 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2190 | #### Sort |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2191 | |
| 2192 | Before the introduction of `sort.Slice`, a common complaint was the |
| 2193 | need for boilerplate definitions in order to use `sort.Sort`. |
| 2194 | With this design, we can add to the sort package as follows: |
| 2195 | |
| 2196 | ```Go |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 2197 | type orderedSlice(type Elem Ordered) []Elem |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2198 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2199 | func (s orderedSlice(Elem)) Len() int { return len(s) } |
| 2200 | func (s orderedSlice(Elem)) Less(i, j int) bool { return s[i] < s[j] } |
| 2201 | func (s orderedSlice(Elem)) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2202 | |
| 2203 | // OrderedSlice sorts the slice s in ascending order. |
| 2204 | // The elements of s must be ordered using the < operator. |
Ian Lance Taylor | ca869dd | 2019-07-31 10:30:47 -0700 | [diff] [blame] | 2205 | func OrderedSlice(type Elem Ordered)(s []Elem) { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2206 | sort.Sort(orderedSlice(Elem)(s)) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2207 | } |
| 2208 | ``` |
| 2209 | |
| 2210 | Now we can write: |
| 2211 | |
| 2212 | ```Go |
| 2213 | sort.OrderedSlice(int32)([]int32{3, 5, 2}) |
| 2214 | ``` |
| 2215 | |
| 2216 | We can rely on type inference to omit the type argument list: |
| 2217 | |
| 2218 | ```Go |
| 2219 | sort.OrderedSlice([]string{"a", "c", "b"}) |
| 2220 | ``` |
| 2221 | |
| 2222 | Along the same lines, we can add a function for sorting using a |
| 2223 | comparison function, similar to `sort.Slice` but writing the function |
| 2224 | to take values rather than slice indexes. |
| 2225 | |
| 2226 | ```Go |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2227 | type sliceFn(type Elem) struct { |
| 2228 | s []Elem |
| 2229 | f func(Elem, Elem) bool |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2230 | } |
| 2231 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2232 | func (s sliceFn(Elem)) Len() int { return len(s.s) } |
| 2233 | func (s sliceFn(Elem)) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) } |
| 2234 | func (s sliceFn(Elem)) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] } |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2235 | |
| 2236 | // SliceFn sorts the slice s according to the function f. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2237 | func SliceFn(type Elem)(s []Elem, f func(Elem, Elem) bool) { |
| 2238 | Sort(sliceFn(Elem){s, f}) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2239 | } |
| 2240 | ``` |
| 2241 | |
| 2242 | An example of calling this might be: |
| 2243 | |
| 2244 | ```Go |
| 2245 | var s []*Person |
| 2246 | // ... |
| 2247 | sort.SliceFn(s, func(p1, p2 *Person) bool { return p1.Name < p2.Name }) |
| 2248 | ``` |
| 2249 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2250 | #### Map keys |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2251 | |
| 2252 | Here is how to get a slice of the keys of any map. |
| 2253 | |
| 2254 | ```Go |
| 2255 | package maps |
| 2256 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2257 | // Keys returns the keys of the map m. |
| 2258 | // Note that map keys (here called type K) must be comparable. |
| 2259 | func Keys(type K, V comparable(K))(m map[K]V) []K { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2260 | r := make([]K, 0, len(m)) |
| 2261 | for k := range m { |
| 2262 | r = append(r, k) |
| 2263 | } |
| 2264 | return r |
| 2265 | } |
| 2266 | ``` |
| 2267 | |
| 2268 | In typical use the types will be inferred. |
| 2269 | |
| 2270 | ```Go |
| 2271 | k := maps.Keys(map[int]int{1:2, 2:4}) // sets k to []int{1, 2} (or {2, 1}) |
| 2272 | ``` |
| 2273 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2274 | #### Map/Reduce/Filter |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2275 | |
| 2276 | Here is an example of how to write map, reduce, and filter functions |
| 2277 | for slices. |
| 2278 | These functions are intended to correspond to the similar functions in |
| 2279 | Lisp, Python, Java, and so forth. |
| 2280 | |
| 2281 | ```Go |
| 2282 | // Package slices implements various slice algorithms. |
| 2283 | package slices |
| 2284 | |
| 2285 | // Map turns a []T1 to a []T2 using a mapping function. |
| 2286 | func Map(type T1, T2)(s []T1, f func(T1) T2) []T2 { |
| 2287 | r := make([]T2, len(s)) |
| 2288 | for i, v := range s { |
| 2289 | r[i] = f(v) |
| 2290 | } |
| 2291 | return r |
| 2292 | } |
| 2293 | |
| 2294 | // Reduce reduces a []T1 to a single value using a reduction function. |
| 2295 | func Reduce(type T1, T2)(s []T1, initializer T2, f func(T2, T1) T2) T2 { |
| 2296 | r := initializer |
| 2297 | for _, v := range s { |
| 2298 | r = f(r, v) |
| 2299 | } |
| 2300 | return r |
| 2301 | } |
| 2302 | |
| 2303 | // Filter filters values from a slice using a filter function. |
| 2304 | func Filter(type T)(s []T, f func(T) bool) []T { |
| 2305 | var r []T |
| 2306 | for _, v := range s { |
| 2307 | if f(v) { |
| 2308 | r = append(r, v) |
| 2309 | } |
| 2310 | } |
| 2311 | return r |
| 2312 | } |
| 2313 | ``` |
| 2314 | |
| 2315 | Example calls: |
| 2316 | |
| 2317 | ```Go |
| 2318 | s := []int{1, 2, 3} |
| 2319 | floats := slices.Map(s, func(i int) float64 { return float64(i) }) |
| 2320 | sum := slices.Reduce(s, 0, func(i, j int) int { return i + j }) |
| 2321 | evens := slices.Filter(s, func(i int) bool { return i%2 == 0 }) |
| 2322 | ``` |
| 2323 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2324 | #### Sets |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2325 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2326 | Many people have asked for Go's builtin map type to be extended, or |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2327 | rather reduced, to support a set type. |
| 2328 | Here is a type-safe implementation of a set type, albeit one that uses |
| 2329 | methods rather than operators like `[]`. |
| 2330 | |
| 2331 | ```Go |
| 2332 | // Package set implements sets of any type. |
| 2333 | package set |
| 2334 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2335 | type Set(type Elem comparable) map[Elem]struct{} |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2336 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2337 | func Make(type Elem comparable)() Set(Elem) { |
| 2338 | return make(Set(Elem)) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2339 | } |
| 2340 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2341 | func (s Set(Elem)) Add(v Elem) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2342 | s[v] = struct{}{} |
| 2343 | } |
| 2344 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2345 | func (s Set(Elem)) Delete(v Elem) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2346 | delete(s, v) |
| 2347 | } |
| 2348 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2349 | func (s Set(Elem)) Contains(v Elem) bool { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2350 | _, ok := s[v] |
| 2351 | return ok |
| 2352 | } |
| 2353 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2354 | func (s Set(Elem)) Len() int { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2355 | return len(s) |
| 2356 | } |
| 2357 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2358 | func (s Set(Elem)) Iterate(f func(Elem)) { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2359 | for v := range s { |
| 2360 | f(v) |
| 2361 | } |
| 2362 | } |
| 2363 | ``` |
| 2364 | |
| 2365 | Example use: |
| 2366 | |
| 2367 | ```Go |
Alec Benzer | ded804a | 2019-12-11 15:39:41 +0000 | [diff] [blame] | 2368 | s := set.Make(int)() |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2369 | s.Add(1) |
| 2370 | if s.Contains(2) { panic("unexpected 2") } |
| 2371 | ``` |
| 2372 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2373 | This example, like the sort examples above, show how to use this |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2374 | design to provide a compile-time type-safe wrapper around an |
| 2375 | existing API. |
| 2376 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2377 | #### Channels |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2378 | |
| 2379 | Many simple general purpose channel functions are never written, |
| 2380 | because they must be written using reflection and the caller must type |
| 2381 | assert the results. |
| 2382 | With this design they become easy to write. |
| 2383 | |
| 2384 | ```Go |
| 2385 | package chans |
| 2386 | |
| 2387 | import "runtime" |
| 2388 | |
| 2389 | // Ranger returns a Sender and a Receiver. The Receiver provides a |
| 2390 | // Next method to retrieve values. The Sender provides a Send method |
| 2391 | // to send values and a Close method to stop sending values. The Next |
| 2392 | // method indicates when the Sender has been closed, and the Send |
| 2393 | // method indicates when the Receiver has been freed. |
| 2394 | // |
| 2395 | // This is a convenient way to exit a goroutine sending values when |
| 2396 | // the receiver stops reading them. |
| 2397 | func Ranger(type T)() (*Sender(T), *Receiver(T)) { |
| 2398 | c := make(chan T) |
Gaal Yahas | 4a530da | 2018-09-01 19:44:20 +0300 | [diff] [blame] | 2399 | d := make(chan bool) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2400 | s := &Sender(T){values: c, done: d} |
| 2401 | r := &Receiver(T){values: c, done: d} |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2402 | runtime.SetFinalizer(r, r.finalize) |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2403 | return s, r |
| 2404 | } |
| 2405 | |
| 2406 | // A sender is used to send values to a Receiver. |
| 2407 | type Sender(type T) struct { |
| 2408 | values chan<- T |
| 2409 | done <-chan bool |
| 2410 | } |
| 2411 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2412 | // Send sends a value to the receiver. It returns whether any more |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2413 | // values may be sent; if it returns false the value was not sent. |
| 2414 | func (s *Sender(T)) Send(v T) bool { |
| 2415 | select { |
| 2416 | case s.values <- v: |
| 2417 | return true |
| 2418 | case <-s.done: |
| 2419 | return false |
| 2420 | } |
| 2421 | } |
| 2422 | |
| 2423 | // Close tells the receiver that no more values will arrive. |
| 2424 | // After Close is called, the Sender may no longer be used. |
| 2425 | func (s *Sender(T)) Close() { |
| 2426 | close(s.values) |
| 2427 | } |
| 2428 | |
| 2429 | // A Receiver receives values from a Sender. |
| 2430 | type Receiver(type T) struct { |
| 2431 | values <-chan T |
| 2432 | done chan<- bool |
| 2433 | } |
| 2434 | |
| 2435 | // Next returns the next value from the channel. The bool result |
| 2436 | // indicates whether the value is valid, or whether the Sender has |
| 2437 | // been closed and no more values will be received. |
| 2438 | func (r *Receiver(T)) Next() (T, bool) { |
| 2439 | v, ok := <-r.values |
| 2440 | return v, ok |
| 2441 | } |
| 2442 | |
| 2443 | // finalize is a finalizer for the receiver. |
| 2444 | func (r *Receiver(T)) finalize() { |
| 2445 | close(r.done) |
| 2446 | } |
| 2447 | ``` |
| 2448 | |
| 2449 | There is an example of using this function in the next section. |
| 2450 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2451 | #### Containers |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2452 | |
| 2453 | One of the frequent requests for generics in Go is the ability to |
| 2454 | write compile-time type-safe containers. |
| 2455 | This design makes it easy to write a compile-time type-safe wrapper |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2456 | around an existing container; we won't write out an example for that. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2457 | This design also makes it easy to write a compile-time type-safe |
| 2458 | container that does not use boxing. |
| 2459 | |
| 2460 | Here is an example of an ordered map implemented as a binary tree. |
| 2461 | The details of how it works are not too important. |
| 2462 | The important points are: |
| 2463 | |
| 2464 | * The code is written in a natural Go style, using the key and value |
| 2465 | types where needed. |
| 2466 | * The keys and values are stored directly in the nodes of the tree, |
| 2467 | not using pointers and not boxed as interface values. |
| 2468 | |
| 2469 | ```Go |
| 2470 | // Package orderedmap provides an ordered map, implemented as a binary tree. |
| 2471 | package orderedmap |
| 2472 | |
| 2473 | import "chans" |
| 2474 | |
| 2475 | // Map is an ordered map. |
| 2476 | type Map(type K, V) struct { |
| 2477 | root *node(K, V) |
| 2478 | compare func(K, K) int |
| 2479 | } |
| 2480 | |
| 2481 | // node is the type of a node in the binary tree. |
| 2482 | type node(type K, V) struct { |
| 2483 | key K |
| 2484 | val V |
| 2485 | left, right *node(K, V) |
| 2486 | } |
| 2487 | |
| 2488 | // New returns a new map. |
| 2489 | func New(type K, V)(compare func(K, K) int) *Map(K, V) { |
| 2490 | return &Map(K, V){compare: compare} |
| 2491 | } |
| 2492 | |
| 2493 | // find looks up key in the map, and returns either a pointer |
| 2494 | // to the node holding key, or a pointer to the location where |
| 2495 | // such a node would go. |
| 2496 | func (m *Map(K, V)) find(key K) **node(K, V) { |
| 2497 | pn := &m.root |
| 2498 | for *pn != nil { |
| 2499 | switch cmp := m.compare(key, (*pn).key); { |
| 2500 | case cmp < 0: |
| 2501 | pn = &(*pn).left |
| 2502 | case cmp > 0: |
| 2503 | pn = &(*pn).right |
| 2504 | default: |
| 2505 | return pn |
| 2506 | } |
| 2507 | } |
| 2508 | return pn |
| 2509 | } |
| 2510 | |
| 2511 | // Insert inserts a new key/value into the map. |
| 2512 | // If the key is already present, the value is replaced. |
| 2513 | // Returns true if this is a new key, false if already present. |
| 2514 | func (m *Map(K, V)) Insert(key K, val V) bool { |
| 2515 | pn := m.find(key) |
| 2516 | if *pn != nil { |
| 2517 | (*pn).val = val |
| 2518 | return false |
| 2519 | } |
| 2520 | *pn = &node(K, V){key: key, val: val} |
| 2521 | return true |
| 2522 | } |
| 2523 | |
| 2524 | // Find returns the value associated with a key, or zero if not present. |
| 2525 | // The found result reports whether the key was found. |
| 2526 | func (m *Map(K, V)) Find(key K) (V, bool) { |
| 2527 | pn := m.find(key) |
| 2528 | if *pn == nil { |
| 2529 | var zero V // see the discussion of zero values, above |
| 2530 | return zero, false |
| 2531 | } |
| 2532 | return (*pn).val, true |
| 2533 | } |
| 2534 | |
| 2535 | // keyValue is a pair of key and value used when iterating. |
| 2536 | type keyValue(type K, V) struct { |
| 2537 | key K |
| 2538 | val V |
| 2539 | } |
| 2540 | |
| 2541 | // InOrder returns an iterator that does an in-order traversal of the map. |
| 2542 | func (m *Map(K, V)) InOrder() *Iterator(K, V) { |
| 2543 | sender, receiver := chans.Ranger(keyValue(K, V))() |
| 2544 | var f func(*node(K, V)) bool |
| 2545 | f = func(n *node(K, V)) bool { |
| 2546 | if n == nil { |
| 2547 | return true |
| 2548 | } |
| 2549 | // Stop sending values if sender.Send returns false, |
| 2550 | // meaning that nothing is listening at the receiver end. |
| 2551 | return f(n.left) && |
| 2552 | sender.Send(keyValue(K, V){n.key, n.val}) && |
| 2553 | f(n.right) |
| 2554 | } |
| 2555 | go func() { |
| 2556 | f(m.root) |
| 2557 | sender.Close() |
| 2558 | }() |
| 2559 | return &Iterator{receiver} |
| 2560 | } |
| 2561 | |
| 2562 | // Iterator is used to iterate over the map. |
| 2563 | type Iterator(type K, V) struct { |
| 2564 | r *chans.Receiver(keyValue(K, V)) |
| 2565 | } |
| 2566 | |
| 2567 | // Next returns the next key and value pair, and a boolean indicating |
| 2568 | // whether they are valid or whether we have reached the end. |
| 2569 | func (it *Iterator(K, V)) Next() (K, V, bool) { |
| 2570 | keyval, ok := it.r.Next() |
| 2571 | if !ok { |
| 2572 | var zerok K |
| 2573 | var zerov V |
| 2574 | return zerok, zerov, false |
| 2575 | } |
| 2576 | return keyval.key, keyval.val, true |
| 2577 | } |
| 2578 | ``` |
| 2579 | |
| 2580 | This is what it looks like to use this package: |
| 2581 | |
| 2582 | ```Go |
| 2583 | import "container/orderedmap" |
| 2584 | |
| 2585 | var m = orderedmap.New(string, string)(strings.Compare) |
| 2586 | |
| 2587 | func Add(a, b string) { |
| 2588 | m.Insert(a, b) |
| 2589 | } |
| 2590 | ``` |
| 2591 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2592 | #### Append |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2593 | |
| 2594 | The predeclared `append` function exists to replace the boilerplate |
| 2595 | otherwise required to grow a slice. |
| 2596 | Before `append` was added to the language, there was a function `Add` |
| 2597 | in the bytes package with the signature |
| 2598 | |
| 2599 | ```Go |
| 2600 | func Add(s, t []byte) []byte |
| 2601 | ``` |
| 2602 | |
| 2603 | that appended two `[]byte` values together, returning a new slice. |
| 2604 | That was fine for `[]byte`, but if you had a slice of some other |
| 2605 | type, you had to write essentially the same code to append more |
| 2606 | values. |
| 2607 | If this design were available back then, perhaps we would not have |
| 2608 | added `append` to the language. |
| 2609 | Instead, we could write something like this: |
| 2610 | |
| 2611 | ```Go |
| 2612 | package slices |
| 2613 | |
| 2614 | // Append adds values to the end of a slice, returning a new slice. |
| 2615 | func Append(type T)(s []T, t ...T) []T { |
| 2616 | lens := len(s) |
| 2617 | tot := lens + len(t) |
| 2618 | if tot <= cap(s) { |
| 2619 | s = s[:tot] |
| 2620 | } else { |
| 2621 | news := make([]T, tot, tot + tot/2) |
| 2622 | copy(news, s) |
| 2623 | s = news |
| 2624 | } |
| 2625 | copy(s[lens:tot], t) |
| 2626 | return s |
| 2627 | } |
| 2628 | ``` |
| 2629 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2630 | That example uses the predeclared `copy` function, but that's OK, we |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2631 | can write that one too: |
| 2632 | |
| 2633 | ```Go |
| 2634 | // Copy copies values from t to s, stopping when either slice is |
| 2635 | // full, returning the number of values copied. |
| 2636 | func Copy(type T)(s, t []T) int { |
| 2637 | i := 0 |
| 2638 | for ; i < len(s) && i < len(t); i++ { |
| 2639 | s[i] = t[i] |
| 2640 | } |
| 2641 | return i |
| 2642 | } |
| 2643 | ``` |
| 2644 | |
| 2645 | These functions can be used as one would expect: |
| 2646 | |
| 2647 | ```Go |
| 2648 | s := slices.Append([]int{1, 2, 3}, 4, 5, 6) |
| 2649 | slices.Copy(s[3:], []int{7, 8, 9}) |
| 2650 | ``` |
| 2651 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2652 | This code doesn't implement the special case of appending or copying a |
| 2653 | `string` to a `[]byte`, and it's unlikely to be as efficient as the |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2654 | implementation of the predeclared function. |
| 2655 | Still, this example shows that using this design would permit append |
| 2656 | and copy to be written generically, once, without requiring any |
| 2657 | additional special language features. |
| 2658 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2659 | #### Metrics |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2660 | |
| 2661 | In a [Go experience |
| 2662 | report](https://medium.com/@sameer_74231/go-experience-report-for-generics-google-metrics-api-b019d597aaa4) |
| 2663 | Sameer Ajmani describes a metrics implementation. |
| 2664 | Each metric has a value and one or more fields. |
| 2665 | The fields have different types. |
| 2666 | Defining a metric requires specifying the types of the fields, and |
| 2667 | creating a value with an Add method. |
| 2668 | The Add method takes the field types as arguments, and records an |
| 2669 | instance of that set of fields. |
| 2670 | The C++ implementation uses a variadic template. |
| 2671 | The Java implementation includes the number of fields in the name of |
| 2672 | the type. |
| 2673 | Both the C++ and Java implementations provide compile-time type-safe |
| 2674 | Add methods. |
| 2675 | |
| 2676 | Here is how to use this design to provide similar functionality in |
| 2677 | Go with a compile-time type-safe Add method. |
| 2678 | Because there is no support for a variadic number of type arguments, |
| 2679 | we must use different names for a different number of arguments, as in |
| 2680 | Java. |
| 2681 | This implementation only works for comparable types. |
| 2682 | A more complex implementation could accept a comparison function to |
| 2683 | work with arbitrary types. |
| 2684 | |
| 2685 | ```Go |
| 2686 | package metrics |
| 2687 | |
| 2688 | import "sync" |
| 2689 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2690 | type Metric1(type T comparable) struct { |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2691 | mu sync.Mutex |
| 2692 | m map[T]int |
| 2693 | } |
| 2694 | |
| 2695 | func (m *Metric1(T)) Add(v T) { |
| 2696 | m.mu.Lock() |
| 2697 | defer m.mu.Unlock() |
| 2698 | if m.m == nil { |
| 2699 | m.m = make(map[T]int) |
| 2700 | } |
| 2701 | m[v]++ |
| 2702 | } |
| 2703 | |
| 2704 | contract cmp2(T1, T2) { |
| 2705 | comparable(T1) |
| 2706 | comparable(T2) |
| 2707 | } |
| 2708 | |
| 2709 | type key2(type T1, T2 cmp2) struct { |
| 2710 | f1 T1 |
| 2711 | f2 T2 |
| 2712 | } |
| 2713 | |
| 2714 | type Metric2(type T1, T2 cmp2) struct { |
| 2715 | mu sync.Mutex |
| 2716 | m map[key2(T1, T2)]int |
| 2717 | } |
| 2718 | |
| 2719 | func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) { |
| 2720 | m.mu.Lock() |
| 2721 | defer m.mu.Unlock() |
| 2722 | if m.m == nil { |
| 2723 | m.m = make(map[key2(T1, T2)]int) |
| 2724 | } |
| 2725 | m[key(T1, T2){v1, v2}]++ |
| 2726 | } |
| 2727 | |
| 2728 | contract cmp3(T1, T2, T3) { |
| 2729 | comparable(T1) |
| 2730 | comparable(T2) |
| 2731 | comparable(T3) |
| 2732 | } |
| 2733 | |
| 2734 | type key3(type T1, T2, T3 cmp3) struct { |
| 2735 | f1 T1 |
| 2736 | f2 T2 |
| 2737 | f3 T3 |
| 2738 | } |
| 2739 | |
| 2740 | type Metric3(type T1, T2, T3 cmp3) struct { |
| 2741 | mu sync.Mutex |
| 2742 | m map[key3(T1, T2, T3)]int |
| 2743 | } |
| 2744 | |
| 2745 | func (m *Metric3(T1, T2, T3)) Add(v1 T1, v2 T2, v3 T3) { |
| 2746 | m.mu.Lock() |
| 2747 | defer m.mu.Unlock() |
| 2748 | if m.m == nil { |
| 2749 | m.m = make(map[key3]int) |
| 2750 | } |
| 2751 | m[key(T1, T2, T3){v1, v2, v3}]++ |
| 2752 | } |
| 2753 | |
| 2754 | // Repeat for the maximum number of permitted arguments. |
| 2755 | ``` |
| 2756 | |
| 2757 | Using this package looks like this: |
| 2758 | |
| 2759 | ```Go |
| 2760 | import "metrics" |
| 2761 | |
| 2762 | var m = metrics.Metric2(string, int){} |
| 2763 | |
| 2764 | func F(s string, i int) { |
| 2765 | m.Add(s, i) // this call is type checked at compile time |
| 2766 | } |
| 2767 | ``` |
| 2768 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2769 | This package implementation has a certain amount of repetition due to |
| 2770 | the lack of support for variadic package type parameters. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2771 | Using the package, though, is easy and type safe. |
| 2772 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2773 | #### List transform |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2774 | |
| 2775 | While slices are efficient and easy to use, there are occasional cases |
| 2776 | where a linked list is appropriate. |
| 2777 | This example primarily shows transforming a linked list of one type to |
| 2778 | another type, as an example of using different instantiations of the |
| 2779 | same parameterized type. |
| 2780 | |
| 2781 | ```Go |
| 2782 | package list |
| 2783 | |
| 2784 | // List is a linked list. |
| 2785 | type List(type T) struct { |
| 2786 | head, tail *element(T) |
| 2787 | } |
| 2788 | |
| 2789 | // An element is an entry in a linked list. |
| 2790 | type element(type T) struct { |
| 2791 | next *element(T) |
| 2792 | val T |
| 2793 | } |
| 2794 | |
| 2795 | // Push pushes an element to the end of the list. |
| 2796 | func (lst *List(T)) Push(v T) { |
| 2797 | if lst.tail == nil { |
| 2798 | lst.head = &element(T){val: v} |
| 2799 | lst.tail = lst.head |
| 2800 | } else { |
| 2801 | lst.tail.next = &element(T){val: v } |
| 2802 | lst.tail = lst.tail.next |
| 2803 | } |
| 2804 | } |
| 2805 | |
| 2806 | // Iterator ranges over a list. |
| 2807 | type Iterator(type T) struct { |
| 2808 | next **element(T) |
| 2809 | } |
| 2810 | |
| 2811 | // Range returns an Iterator starting at the head of the list. |
| 2812 | func (lst *List(T)) Range() *Iterator(T) { |
| 2813 | return Iterator(T){next: &lst.head} |
| 2814 | } |
| 2815 | |
| 2816 | // Next advances the iterator. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2817 | // It returns whether there are more elements. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2818 | func (it *Iterator(T)) Next() bool { |
| 2819 | if *it.next == nil { |
| 2820 | return false |
| 2821 | } |
| 2822 | it.next = &(*it.next).next |
| 2823 | return true |
| 2824 | } |
| 2825 | |
| 2826 | // Val returns the value of the current element. |
| 2827 | // The bool result reports whether the value is valid. |
| 2828 | func (it *Iterator(T)) Val() (T, bool) { |
| 2829 | if *it.next == nil { |
| 2830 | var zero T |
| 2831 | return zero, false |
| 2832 | } |
| 2833 | return (*it.next).val, true |
| 2834 | } |
| 2835 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2836 | // Transform runs a transform function on a list returning a new list. |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2837 | func Transform(type T1, T2)(lst *List(T1), f func(T1) T2) *List(T2) { |
| 2838 | ret := &List(T2){} |
| 2839 | it := lst.Range() |
| 2840 | for { |
| 2841 | if v, ok := it.Val(); ok { |
| 2842 | ret.Push(f(v)) |
| 2843 | } |
| 2844 | it.Next() |
| 2845 | } |
| 2846 | return ret |
| 2847 | } |
| 2848 | ``` |
| 2849 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2850 | #### Context |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2851 | |
| 2852 | The standard "context" package provides a `Context.Value` method to |
| 2853 | fetch a value from a context. |
| 2854 | The method returns `interface{}`, so using it normally requires a type |
| 2855 | assertion to the correct type. |
| 2856 | Here is an example of how we can add type parameters to the "context" |
| 2857 | package to provide a type-safe wrapper around `Context.Value`. |
| 2858 | |
| 2859 | ```Go |
| 2860 | // Key is a key that can be used with Context.Value. |
| 2861 | // Rather than calling Context.Value directly, use Key.Load. |
| 2862 | // |
| 2863 | // The zero value of Key is not ready for use; use NewKey. |
| 2864 | type Key(type V) struct { |
| 2865 | name string |
| 2866 | } |
| 2867 | |
| 2868 | // NewKey returns a key used to store values of type V in a Context. |
| 2869 | // Every Key returned is unique, even if the name is reused. |
| 2870 | func NewKey(type V)(name string) *Key { |
| 2871 | return &Key(V){name: name} |
| 2872 | } |
| 2873 | |
| 2874 | // WithValue returns a new context with v associated with k. |
| 2875 | func (k *Key(V)) WithValue(parent Context, v V) Context { |
| 2876 | return WithValue(parent, k, v) |
| 2877 | } |
| 2878 | |
| 2879 | // Value loads the value associated with k from ctx and reports |
| 2880 | //whether it was successful. |
| 2881 | func (k *Key(V)) Value(ctx Context) (V, bool) { |
| 2882 | v, present := ctx.Value(k).(V) |
| 2883 | return v.(V), present |
| 2884 | } |
| 2885 | |
| 2886 | // String returns the name and expected value type. |
| 2887 | func (k *Key(V)) String() string { |
| 2888 | var v V |
| 2889 | return fmt.Sprintf("%s(%T)", k.name, v) |
| 2890 | } |
| 2891 | ``` |
| 2892 | |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2893 | To see how this might be used, consider the net/http package's |
Russ Cox | 00fd2f6 | 2018-08-23 15:11:30 -0400 | [diff] [blame] | 2894 | `ServerContextKey`: |
| 2895 | |
| 2896 | ```Go |
| 2897 | var ServerContextKey = &contextKey{"http-server"} |
| 2898 | |
| 2899 | // used as: |
| 2900 | ctx := context.Value(ServerContextKey, srv) |
| 2901 | s, present := ctx.Value(ServerContextKey).(*Server) |
| 2902 | ``` |
| 2903 | |
| 2904 | This could be written instead as |
| 2905 | |
| 2906 | ```Go |
| 2907 | var ServerContextKey = context.NewKey(*Server)("http_server") |
| 2908 | |
| 2909 | // used as: |
| 2910 | ctx := ServerContextKey.WithValue(ctx, srv) |
| 2911 | s, present := ServerContextKey.Value(ctx) |
| 2912 | ``` |
| 2913 | |
| 2914 | Code that uses `Key.WithValue` and `Key.Value` instead of |
| 2915 | `context.WithValue` and `context.Value` does not need any type |
| 2916 | assertions and is compile-time type-safe. |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2917 | |
| 2918 | #### Dot product |
| 2919 | |
| 2920 | A generic dot product implementation that works for slices of any |
| 2921 | numeric type. |
| 2922 | |
| 2923 | ```Go |
| 2924 | // Numeric is a contract that matches any numeric type. |
| 2925 | // It would likely be in a contracts package in the standard library. |
| 2926 | contract Numeric(T) { |
| 2927 | T int, int8, int16, int32, int64, |
| 2928 | uint, uint8, uint16, uint32, uint64, uintptr, |
| 2929 | float32, float64, |
| 2930 | complex64, complex128 |
| 2931 | } |
| 2932 | |
| 2933 | func DotProduct(type T Numeric)(s1, s2 []T) T { |
| 2934 | if len(s1) != len(s2) { |
| 2935 | panic("DotProduct: slices of unequal length") |
| 2936 | } |
| 2937 | var r T |
| 2938 | for i := range s1 { |
| 2939 | r += s1[i] * s2[i] |
| 2940 | } |
| 2941 | return r |
| 2942 | } |
| 2943 | ``` |
| 2944 | |
| 2945 | #### Absolute difference |
| 2946 | |
| 2947 | Compute the absolute difference between two numeric values, by using |
| 2948 | an `Abs` method. |
| 2949 | This uses the same `Numeric` contract defined in the last example. |
| 2950 | |
| 2951 | This example uses more machinery than is appropriate for the simple |
| 2952 | case of computing the absolute difference. |
| 2953 | It is intended to show how the common part of algorithms can be |
| 2954 | factored into code that uses methods, where the exact definition of |
| 2955 | the methods can very based on the kind of type being used. |
| 2956 | |
| 2957 | ```Go |
| 2958 | // NumericAbs matches numeric types with an Abs method. |
| 2959 | contract NumericAbs(T) { |
| 2960 | Numeric(T) |
| 2961 | T Abs() T |
| 2962 | } |
| 2963 | |
| 2964 | // AbsDifference computes the absolute value of the difference of |
| 2965 | // a and b, where the absolute value is determined by the Abs method. |
| 2966 | func AbsDifference(type T NumericAbs)(a, b T) T { |
| 2967 | d := a - b |
| 2968 | return d.Abs() |
| 2969 | } |
| 2970 | ``` |
| 2971 | |
| 2972 | We can define an `Abs` method appropriate for different numeric types. |
| 2973 | |
| 2974 | ```Go |
| 2975 | // OrderedNumeric matches numeric types that support the < operator. |
| 2976 | contract OrderedNumeric(T) { |
| 2977 | T int, int8, int16, int32, int64, |
| 2978 | uint, uint8, uint16, uint32, uint64, uintptr, |
| 2979 | float32, float64 |
| 2980 | } |
| 2981 | |
| 2982 | // Complex matches the two complex types, which do not have a < operator. |
| 2983 | contract Complex(T) { |
| 2984 | T complex64, complex128 |
| 2985 | } |
| 2986 | |
| 2987 | // OrderedAbs is a helper type that defines an Abs method for |
| 2988 | // ordered numeric types. |
| 2989 | type OrderedAbs(type T OrderedNumeric) T |
| 2990 | |
Robert Griesemer | 8a1c549 | 2019-12-19 13:56:06 -0800 | [diff] [blame] | 2991 | func (a OrderedAbs(T)) Abs() OrderedAbs(T) { |
Ian Lance Taylor | 4a54a00 | 2019-07-26 23:21:48 -0700 | [diff] [blame] | 2992 | if a < 0 { |
| 2993 | return -a |
| 2994 | } |
| 2995 | return a |
| 2996 | } |
| 2997 | |
| 2998 | // ComplexAbs is a helper type that defines an Abs method for |
| 2999 | // complex types. |
| 3000 | type ComplexAbs(type T Complex) T |
| 3001 | |
| 3002 | func (a ComplexAbs(T)) Abs() T { |
| 3003 | r := float64(real(a)) |
| 3004 | i := float64(imag(a)) |
| 3005 | d := math.Sqrt(r * r + i * i) |
| 3006 | return T(complex(d, 0)) |
| 3007 | } |
| 3008 | ``` |
| 3009 | |
| 3010 | We can then define functions that do the work for the caller by |
| 3011 | converting to and from the types we just defined. |
| 3012 | |
| 3013 | ```Go |
| 3014 | func OrderedAbsDifference(type T OrderedNumeric)(a, b T) T { |
| 3015 | return T(AbsDifference(OrderedAbs(T)(a), OrderedAbs(T)(b))) |
| 3016 | } |
| 3017 | |
| 3018 | func ComplexAbsDifference(type T Complex)(a, b T) T { |
| 3019 | return T(AbsDifference(ComplexAbs(T)(a), ComplexAbs(T)(b))) |
| 3020 | } |
| 3021 | ``` |
| 3022 | |
| 3023 | It's worth noting that this design is not powerful enough to write |
| 3024 | code like the following: |
| 3025 | |
| 3026 | ```Go |
| 3027 | // This function is INVALID. |
| 3028 | func GeneralAbsDifference(type T Numeric)(a, b T) T { |
| 3029 | switch a.(type) { |
| 3030 | case int, int8, int16, int32, int64, |
| 3031 | uint, uint8, uint16, uint32, uint64, uintptr, |
| 3032 | float32, float64: |
| 3033 | return OrderedAbsDifference(a, b) // INVALID |
| 3034 | case complex64, complex128: |
| 3035 | return ComplexAbsDifference(a, b) // INVALID |
| 3036 | } |
| 3037 | } |
| 3038 | ``` |
| 3039 | |
| 3040 | The calls to `OrderedAbsDifference` and `ComplexAbsDifference` are |
| 3041 | invalid, because not all the types that satisfy the `Numeric` contract |
| 3042 | can satisfy the `OrderedNumeric` or `Complex` contracts. |
| 3043 | Although the type switch means that this code would conceptually work |
| 3044 | at run time, there is no support for writing this code at compile |
| 3045 | time. |
| 3046 | This another of way of expressing one of the omissions listed above: |
| 3047 | this design does not provide for specialization. |