Note, I've already looked at https://golang.org/issue/28987, 
https://golang.org/issue/19814, and https://golang.org/issue/28438. All of 
them require them to make a change that breaks the compatibility promise. I 
want to explore another way of defining enums that is backward compatible.

*Enums as types with constrained values*

Say we have an enum for mathematical operations, we can define it currently 
with iota like so:

```go
type Operation int
const (
  Uknown Operation = iota // zero value
  Sum
  Difference
  Product
  Difference
)
```

There are a few problems here:
1. `Operation(100)` is valid even though it shouldn't be. Sure, we can use 
static analysis tools but it'd be a lot better if the compiler checked this 
for us.
2. Although I've never seen anyone do it, you can redefine the same values 
in a different place with different names.
3. Zero values can become complicated easily.
4. Very often we want enums with string representations. We can define a 
`.String()` method that uses a map, but it could be considered boilerplate. 
For example, we would need to define the below method for the `Operation` 
type

```go
func (o Operation) String() string {
  symbols := map[Operation]string{
    Sum:  "+" ,
    Product: "*",
    Difference: "-",
    Quotient: "/",
  }

  return symbols[o] // assuming we know `o` is always a valid value
}
```

I think a much better solution would be the below syntax

```go
type Operation string (
  Sum = "+"
  Product = "*"
  Difference = "-"
  Quotient = "%"
)
```

We can think of Operation as a type with an underlying type of string with 
"constrained values", i.e. any variable with a type of `Operation` can only 
have one of these predefined values. We can define constrained values 
similarly for any type that is comparable, but not pointers.

*Usage*

```go
// using one of the values
var operator = Operator.Sum

// converting a string constant to the enum
var operator2 = Operator("+") // if the value we're trying to convert is 
not a variable or doesn't have a component that's a variable, the validity 
could be checked by the compiler

// conversion of a dynamic value
var str = "foo"
var operator3 = Operator(str) // panics if invalid value?
```

We get compiler time type checking, only certain values are allowed at both 
compile-time and runtime (which is the point of an enum).

Downsides:
1. This could be considered syntactic sugar for `iota`, and `.String()`
2. The syntax looks out of place? This could be discussed further
3. This could potentially add complexity but IMO it's very little

Pros:
1. Better type safety 
2. Fixes the problems with the current approach to enums mentioned above
3. Doesn't violate the compatibility guidelines
4. No complex enums like the ones you'd find in other languages like Rust 
or Swift (specially swift)

*Some more examples*

```go
// the default underlying values would be A=0, B=1, and so on...
type Foo int (
  A
  B
  C
  D = 10 // custom value
)

// the underlying default values are "A", "B", and so on...
type Bar string (
  A
  B
  C
  D = "custom_value"
)

type FooS struct {
  Foo string
  Bar string
}

// no default values in this case, have to provide a value
type Foo2 FooS (
  A = FooS{"a", "b"}
  B = FooS{"c", "d"}
)
```

*Zero Values*

There are a few ways we handle zero values.

1. We enforce declaring a zero value
```go
type Foo struct {
  foo string
}

type Bar Foo (
  A = Foo{} // enforce this
  B = Foo{"another"}
)

type Bar2 int {
  A // zero value like the first value of iota
  B
}

type Bar3 string {
  A = "" // enforce this
  B
}
```

2. Create an `<type-name>.Zero` by default
An option, but it doesn't feel right.

3. No special naming or requirement for zero values
```go
// we can just have
type Foo string (
  A
  B
)

var foo = Foo("") // this represents a zero value and is allowed even 
though we don't have a value that contains a zero value
```

We shouldn't allow `nil` considering the underlying types cannot be `nil`.











-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/fec12ef6-0c05-4164-9d97-ab1d4acc9a6an%40googlegroups.com.

Reply via email to