Repository: go
Issue: proposal: spec: add typed enum support
Labels: LanguageChange,Proposal,LanguageChangeReview

I'd like to propose that enum be added to Go as a special kind of `type`. The examples below are borrowed from the protobuf example.

### Enums in Go today
```
type SearchRequest int
var (
	SearchRequestUNIVERSAL SearchRequest = 0 // UNIVERSAL
	SearchRequestWEB       SearchRequest = 1 // WEB
	SearchRequestIMAGES    SearchRequest = 2 // IMAGES
	SearchRequestLOCAL     SearchRequest = 3 // LOCAL
	SearchRequestNEWS      SearchRequest = 4 // NEWS
	SearchRequestPRODUCTS  SearchRequest = 5 // PRODUCTS
	SearchRequestVIDEO     SearchRequest = 6 // VIDEO
)

type SearchRequest string
var (
	SearchRequestUNIVERSAL SearchRequest = "UNIVERSAL"
	SearchRequestWEB       SearchRequest = "WEB"
	SearchRequestIMAGES    SearchRequest = "IMAGES"
	SearchRequestLOCAL     SearchRequest = "LOCAL"
	SearchRequestNEWS      SearchRequest = "NEWS"
	SearchRequestPRODUCTS  SearchRequest = "PRODUCTS"
	SearchRequestVIDEO     SearchRequest = "VIDEO"
)

// IsValid has to be called everywhere input happens, or you risk bad data - no guarantees
func (sr SearchRequest) IsValid() bool {
	switch sr {
		case SearchRequestUNIVERSAL, SearchRequestWEB...:
			return true
	}
	return false
}
```

### How it might look with language support
```
enum SearchRequest int {
    0 // UNIVERSAL
    1 // WEB
    2 // IMAGES
    3 // LOCAL
    4 // NEWS
    5 // PRODUCTS
    6 // VIDEO
}

enum SearchRequest string {
    "UNIVERSAL"
    "WEB"
    "IMAGES"
    "LOCAL"
    "NEWS"
    "PRODUCTS"
    "VIDEO"
}
```

The pattern is common enough that I think it warrants special casing, and I believe that it makes code more readable. At the implementation layer, I would imagine that the majority of cases can be checked at compile time, some of which already happen today, while others are near impossible or require significant tradeoffs.
 - **Safety for exported types**: nothing prevents someone from doing `SearchRequest(99)` or `SearchRequest("MOBILEAPP")`. Current workarounds include making an unexported type with options, but that often makes the resulting code harder to use / document.
 - **Runtime safety**: Just like protobuf is going to check for validity while unmarshaling, this provides language wide validation, anytime that an enum is instantiated.
 - **Tooling / Documentation**: many packages today put valid options into field comments, but not everyone does it and there is no guarantee that the comments aren't outdated.

### Things to Consider
 - **Nil**: by implementing `enum` on top of the type system, I don't believe this should require special casing.  If someone wants `nil` to be valid, then the enum should be defined as a pointer.
 - **Default value / runtime assignments**: This is one of the tougher decisions to make. What if the Go default value isn't defined as a valid enum? Static analysis can mitigate some of this at compile time, but there would need to be a way to handle outside input.

I don't have any strong opinions on the syntax. I do believe this could be done well and would make a positive impact on the ecosystem.