7 ms·
The issue is that it's more or less impossible to graft onto the language now. You could add enums, but the main reason why people want them is to fix the error
by TwentyPosts 2y ago
The issue is that it's more or less impossible to graft onto the language now. You could add enums, but the main reason why people want them is to fix the error handling. You can't do this without fracturing the ecosystem.
- foobiekr 2y agos/fracturing/having to use older libraries in the existing manner/
- drdaeman 2y ago> but the main reason why people want them is to fix the error handling Why do you think so? Maybe I'm an odd case, but my main use case for enums is for APIs and database designs, where I want to lock down some field to a set of acceptable values and make sure anything else is a mistake. Or for state machines. Error handling is manageable without enums (but I love Option/Result types more than Go's error approach, especially with the ? operator).
- sethammons 2y agoWhen enums make it from the language to the db, things are now brittle and it only takes one intern to sort the enums alphabetically to destroy the look up relations. An enum look up table helps, but now they are not enums in the language.
- randomdata 2y ago> my main use case for enums is for APIs and database designs, where I want to lock down some field to a set of acceptable values and make sure anything else is a mistake Then what you are really looking for is sum types (what Rust calls enums, but unusually so), not enums. Go does not have sum types, but you can use interfaces to archive a rough facsimile and most certainly to satisfy your specific expectation: type Hot struct{} func (Hot) temp() {} type Cold struct{} func (Cold) temp() {} type Temperature interface { temp() } func SetThermostat(temperature Temperature) { switch temperature.(type) { case Hot: fmt.Println("Hot") case Cold: fmt.Println("Cold") } }
- blueberry87 2y agoannoyingly go can't have proper sum types, as the requirement for a default value for everything doesn't make any sense for sum types
- andyferris 2y agoYou can just default to the first variant, no?
- throwaway143829 2y agoCouldn't the zero value be nil? I get that some types like int are not nil-able, but the language allows you to assign both nil and int to a value of type any (interface{}), so I wonder why it couldn't work the same for sum types. i.e. they would be a subset of the `any` type.
- randomdata 2y agoSaid "requirement" is only a human construct. The computer doesn't care. If the humans choose to make an exception for that, it can be done. Granted, the planning that has taken place thus far has rejected such an exception, but there is nothing about Go that fundamentally prevents carving out an exception.
- throwaway143829 2y agoEnums and sum types seem to be related. In the code you wrote, you could alternatively express the Hot and Cold types as enum values. I would say that enums are a subset of sum types but I don't know if that's quite right. I guess maybe if you view each enum value as having its own distinct type (maybe a subtype of the enum type), then you could say the enum is the sum type of the enum value types?
- randomdata 2y ago> Enums and sum types seem to be related. They can certainly help solve some of the same problems. Does that make them related? I don't know. By definition, an enumeration is something that counts one-by-one. In other words, as is used in programming languages, a construct that numbers a set of named constants. Indeed you can solve the problem using that: type Temperature int const ( Hot Temperature = iota Cold ) func SetThermostat(temperature Temperature) { switch temperature { case Hot: fmt.Println("Hot") case Cold: fmt.Println("Cold") } } But, while a handy convenience (especially if the set is large!), you don't even need enums. You can number the constants by hand to the exact same effect: type Temperature int const ( Hot Temperature = 0 Cold Temperature = 1 ) func SetThermostat(temperature Temperature) { switch temperature { case Hot: fmt.Println("Hot") case Cold: fmt.Println("Cold") } } I'm not sure that exhibits any sum type properties. I guess you could see the value as being a tag, but there is no union.
- imetatroll 2y agohttps://www.postgresql.org/docs/current/datatype-enum.html https://www.postgresql.org/docs/current/datatype-enum.html Then wrap appropriately. Something like sqlc will actually generate everything you need.
- Kamq 2y ago> but I love Option/Result types more than Go's error approach The thing is, these don't add much on their own. You'd have to bring in pattern matching and/or a bunch of other things* that would significantly complicate the language. For example, with what's currently in the language, you could definitely have an option type. You'd just be limited to roughly an api that's `func (o Option[T]) IsEmpty() bool` and `func (o Option[T]) Get() T`. And these would just check if the underlying point is nil and dereference it. You can already do that with pointers. Errors/Result are similar. A `try` keyword that expands `x := try thingThatProducesErr()` to: x, err := thingThatProducesErr() if err != nil { return {zero values of the rest of the function signature}, err } Might be more useful in go (you could have a similar one for pointers). * at the very least generic methods for flat map shenanigans
- Mawr 2y agoUsing an Option instead of a pointer buys you the inability to forget to check for nil. Just need to make sure the Option exposes the internal value only through: func (o Option[Value]) Get() (Value, bool) { return o.value, o.exists } Accessing the value is then forced to look like this: if value, ok := option.Get(); ok { // value is valid } // value is invalid Thus, there's no possibility of an accidental nil pointer dereference, which I think is a big win. A Result type would bring a similar benefit of fixing the few edge cases where an error may accidentally not be handled. Although I don't think it'd be worth the cost of switching over.
- Hendrikto 2y agoHow is that better than if value != nil { // value is valid } // value is invalid ? Of course, this is often left out, but you can just as easily do: value, _ := option.Get() So this is just not true: > Using an Option instead of a pointer buys you the inability to forget to check for nil.
- Mawr 2y agoIt's better because you do not need to remember to check for nil, the compiler will remind you every time by erroring out until you handle the second return value of `option.Get()`. > Of course, this is often left out, but you can just as easily do: Unfortunately it gets brought up pretty much every time in these discussions. Deliberate attempts to circumvent safety are not part of the threat model. The goal is prevention of accidental mistakes. Nothing can ultimately stop you from disabling all safeties, pointing the shotgun at your foot and pulling the trigger.
- LorenzoGood 2y agoI just want regular enums, that would solve the problems that result from using the current status quo.
- jhoechtl 2y agoI would like to have proper stack traces. With that the error handling in go would be fixed.
- sethammons 2y agoYou can emit a stack trace anytime you like
- foldr 2y agoDepends what you mean by 'enums' exactly, but now that generics has been added, a small change would be to allow interfaces defined via type disjunction to be used as concrete types: type Option1 struct { ... } type Option2 struct { ... } type MyEnum interface { Option1 | Option2 } var myValue MyEnum // currently not legal Go That doesn't solve all the use cases for enums / sum types, but it would be useful.