-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: spec: add typed enum support #19814
Comments
@derekparker there's a discussion for making a Go2 proposal in #19412 |
I read through that earlier today, but that seemed more focused on valid types, where this is focused on valid type values. Maybe this is a subset of that proposal, but also is a less far-reaching change to the type system that could be put into Go today. |
enums are a special case of sum types where all the types are the same and there's a value associated to each by a method. More to type, surely, but same effect. Regardless, it would be one or the other, sum types cover more ground, and even sum types are unlikely. Nothing's happening until Go2 because of the Go1 compatibility agreement, in any case, since these proposals would, at the very least, require a new keyword, should any of them be accepted |
Fair enough, but neither of these proposals is breaking the compatibility agreement. There was an opinion expressed that sum types were "too big" to add to Go1. If that's the case, then this proposal is a valuable middle ground that could be a stepping stone to full sum types in Go2. |
They both require a new keyword which would break valid Go1 code using that as an identifier |
I think that could be worked around |
A new language feature needs compelling use cases. All language features are useful, or nobody would propose them; the question is: are they useful enough to justify complicating the language and requiring everyone to learn the new concepts? What are the compelling use cases here? How will people use these? For example, would people expect to be able to iterate over the set of valid enum values, and if so how would they do that? Does this proposal do more than let you avoid adding default cases to some switches? |
Here's the idiomatic way of writing enumerations in current Go: type SearchRequest int
const (
Universal SearchRequest = iota
Web
Images
Local
News
Products
Video
) This has the advantage that it's easy to create flags that can be OR:ed (using operator type SearchRequest int
const (
Universal SearchRequest = 1 << iota
Web
Images
Local
News
Products
Video
) I can't see that introducing a keyword |
@md2perpe that isn't enums.
package main
import (
"fmt"
)
func main() {
type SearchRequest int
const (
Universal SearchRequest = iota
Web
)
const (
Another SearchRequest = iota
Foo
)
fmt.Println("Should be false: ", (Web == Foo))
// Prints: "Should be false: true"
} I totally agree with @derekperkins that Go needs some enum as first class citizen. How that would look like, I'm not sure, but I suspect it could be done without breaking the Go 1 glass house. |
@md2perpe
As soon as you need to represent a string or another type, which is very common for external flags, This isn't just an convenience issue to make code shorter. This is a proposal that will allow for data integrity in a way that is not enforceable by the language today. |
I think that is a solid use case, as mentioned by @bep. I think the iteration would look like a standard Go loop, and I think they would loop in the order that they were defined.
|
If Go were to add anything more than iota, at that point why not go for algebraic data types? |
By extension of ordering according to the definition order, and following the example of protobuf, I think that the default value of the field would be the first defined field. |
@bep Not as convenient, but you can get all these properties:
|
I don't think compile-time checked enums are a good idea. I believe go pretty much has this right right now. My reasoning is
Overall, just a huge -1 for me. Not only doesn't it add anything; it actively hurts. |
I think current enum implementation in Go is very straightforward and provides enough compilation time checks. I actually expect some kind of Rust enums with basic pattern matching, but it possibly breaks Go1 guaranties. |
Since enums are a special case of sum types and the common wisdom is that we should use interfaces to simulate sum types the answer is clearly https://play.golang.org/p/1BvOakvbj2 (if it's not clear: yes, that is a joke—in classic programmer fashion, I'm off by one). In all seriousness, for the features discussed in this thread, some extra tooling would be useful. Like the stringer tool, a "ranger" tool could generate the equivalent of the Something could generate {Binary,Text}{Marshaler,Unmarshaler} implementations to make them easier to send over the wire. I'm sure there are a lot of little things like this that would be quite useful on occasion. There are some vetting/linter tools for exhaustiveness checking of sum types simulated with interfaces. No reason there couldn't be ones for iota enums that tell you when cases are missed or invalid untyped constants are used (maybe it should just report anything other than 0?). There's certainly room for improvement on that front even without language changes. |
Enums would complement the already established type system. As the many examples in this issue have shown, the building blocks for enums is already present. Just as channels are high level abstractions build on more primitives types, enums should be built in the same manner. Humans are arrogant, clumsy, and forgetful, mechanisms like enums help human programmers make less programming errors. |
@bep I have to disagree with all three of your points. Go idiomatic enums strongly resemble C enums, which do not have any iteration of valid values, do not have any automatic conversion to strings, and do not have necessarily distinct identity. Iteration is nice to have, but in most cases if you want iteration, it is fine to define constants for the first and last values. You can even do so in a way that does not require updating when you add new values, since Automatic conversion to string is only a small value: especially in this proposal, the string values need to be written to correspond to the int values, so there is little to be gained over explicitly writing an array of string values yourself. In an alternate proposal, it could be worth more, but there are downsides to forcing variable names to correspond to string representations as well. Finally, distinct identity I'm not even sure is a useful feature at all. Enums are not sum types as in, say, Haskell. They are named numbers. Using enums as flag values, for instance, is common. For instance, you can have |
@alercah, please don't pull this Go was built to be a better C/C++ or a less verbose Java, so comparing it to the latter would make more sense. And Java does have a built-in And, while you may disagree with the "much more powerful part", the Java I can appreciate the argument that Go is leaner, simpler etc., and that some compromise must be taken to keep it this way, and I have seen some hacky workarounds in this thread that kind of works, but a set of |
Enumerations and automatic string conversions are good candidates for the 'go generate' feature. We have some solutions already. Java enums are something in the middle of classic enums and sum types. So it is a bad language design in my opinion.
That was true a decade ago. See modern zero-cost implementation of Option in Rust powered by sum types and pattern matching. |
Note that I don't disagree too much with the conclusions given here, but the use of _ idiomatic Go_ is putting Go up on som artsy pedestal. Most software programming is fairly boring and practical. And often you just need to populate a drop-down box with an enum ... |
|
@bep I think you misread the original comment. "Go idiomatic enums" was supposed to refer to the current construction of using type Foo int + const-decl + iota, I believe, not to say "whatever you are proposing isn't idiomatic". |
@rsc Regarding the If my proposal of a new reserved keyword |
A new keyword is not possible before Go 2. It would be a clear violation of the Go 1 compatibility guarantee. Personally, I am not yet seeing the compelling arguments for enum, or, for that matter, for sum types, even for Go 2. I'm not saying they can't happen. But one of the goals of the Go language is simplicity of the language. It's not enough for a language feature to be useful; all language features are useful--if they weren't useful, nobody would propose them. In order to add a feature to Go the feature has to have enough compelling use cases to make it worth complicating the language. The most compelling use cases are code that can not be written without the feature, at least now without great awkwardness. |
Actually, the IDE I use provides good autocomplete suggestions based on the type of the constants.
A new keyword breaks compatibility, as existing code using an identifier with the same name as the keyword would no longer compile. |
"Nobody would accidentally do ..." The intention of a good compiler is supposed to prevent even the case of such cases in the first place. Like, cases such as #51317 are heavily debated just because of making sure the compiler and the runtime handles the case in a way that the user does not have to care or will be safe from their own actions. Like, I see all the time arguments from both ways.
always depending on which side of the argument you're on. If Go strives to be safer to use, then you should prevent a user from having such weird cases such as that. And there are cases and have seen or I'm not sure even have written myself I won't put myself in another "im the best programmer" bag, because it was "easy" or comfortable at the time to do so and because the programmer just didn't think too much about it. Where an pseudo-enumeration was written like that in an unsafe manner. |
I may be genuinely wrong, and just curious, how would I don't know, let me know what you think if it would actually break compatibility |
This may be the intention of a proof assistant, but not of a general-purpose programming language compiler. The latter must strike a balance between often contradictory requirements. It should aim to guard against common accidental mistakes, not against deliberate self-sabotage. Otherwise, the compiler wouldn't compile statements like |
I see what you mean, I think you're presenting though two completely separate cases:
edit: Despite that statement, I presented a ton of arguments about enums, let's not narrow it down to just |
This program doesn't compile, even though
Go's parsing of keywords is deliberately context-free. |
@exapsy In regards to backwards compatibility, you need to understand the difference between the scanner and the parser. The scanner takes bytes and converts them into a stream of tokens which each has a kind. A keyword (like The parser then takes the stream of tokens and converts it into an abstract syntax tree, based on the kind of the token. "A function declaration is a If we would introduce a new keyword like To make the parser still be able to parse More problematically, currently a type declaration All of that being said: Yes, today we could probably decide to do it, by guarding it behind a new language version. That is, we would make In particular, we set ourselves the limit that an existing valid program should never be re-interpreted as a different valid program - it should always either break in a new version, or the new version should not be valid today. This is to make it easier to detect these kinds of backwards-incompatible changes automatically - if your program still compiles with the new version, it should do the same thing. A new syntactical construct should be carefully vetted to fulfill that requirement. But yes, we could probably do that today. Note that a lot of this discussion predates having these tools for introducing backwards-incompatible changes in place. On a meta-level: In my experience, when discussions on GitHub issues get new comments with too high a frequency, that is a symptom that the discussion is not being productive. While I typed out this relatively long comment, to give a thorough explanation of all the subtleties, at least two new comments popped up above. Perhaps making parts of my thorough explanation obsolete, but probably not. But it's better to take your time to be thoughtful, in this medium. If new comments happen every few minutes (by the same two or three people) that is a clear indication that the discussion should be taken to a medium more suited for real-time communication (like Slack). |
The Go team discussed the need for enums in the very early days of Go. Enums pose various of problems, some of which have been pointed out here. Extensibility of enums is a real issue. Scope of enum names is another real issue. Orthogonality of concepts is also a concern (enum vs constants vs variables). For these reasons we decided early on not to add a specific enum concept to Go and instead use the There's also no urgent need for enums or an inability to do things in Go that cannot be done relatively easily with existing mechanisms. In short, the bar is high for a significant change such as this, and it doesn't seem that the bar is met. With respect to support for more complex typed "enums", rather than introducing a heavy-weight mechanism, I suggest we investigate #21473 ( |
[like] WeiYaPeng(Jim) reacted to your message:
…________________________________
From: Apostolis A. ***@***.***>
Sent: Sunday, January 14, 2024 7:13:56 AM
To: golang/go ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [golang/go] proposal: spec: add typed enum support (#19814)
simplicity of the language.
Enums are supposed to make it simpler. I don't find this
type Country string
var (
CountryGreatBritain = iota + 1
CountryGreece
...
)
which does not have any type-safety guarantees, any compilation features, good autocomplete features etc.
How is that more compelling and simple than this
enum Country {
GreatBritain = 1
Greece
....
}
Im not sure, I could be entirely wrong, I'm not sure I understand the "new keyword breaks compatibility" narrative?
To me if feels like creating a new lib into the stdlib. As in, it's something new, if somebody doesn't use it, how would it break compatibility?
Even if it breaks compatibility somehow, in a way I still don't understand exactly, why not just place it under Go v2 then
Enums have been supported by every language every since C. My understanding is that they have been a great type-safety tool, non-package namespace bloating technique for a long time.
Personally I don't find any good arguments for not supporting enums. It's a keyword that has made people's life easier, that helps humans not mess up, makes a language simpler by not having a) Bloated namespace b) Non-type safety for Go's pseudo-enum techniques c) Compiler making sure you don't mess up d) People use your libraries safely and many other reasons.
My understanding is that Go's goal is to be a simple language. And Enums, for the end user, seem to achieve just that goal on every aspect.
—
Reply to this email directly, view it on GitHub<#19814 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAKTSGNL5FII2YOBBCVYSOLYOOATJAVCNFSM4DGA7O32U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBZGA4DMOBXGUYA>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
[like] WeiYaPeng(Jim) reacted to your message:
…________________________________
From: Apostolis A. ***@***.***>
Sent: Sunday, January 14, 2024 9:12:25 AM
To: golang/go ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [golang/go] proposal: spec: add typed enum support (#19814)
... { NewPerson(Country(15) } // should NOT be compiled, but compiles, no type-safety
Nobody would accidentally make up a meaningless number except for a deliberate intention to hurt oneself. I think this is a theoretical problem that doesn't happen in practice.
"Nobody would accidentally do ..."
The intention of a good compiler is supposed to prevent even the case of such cases in the first place. Like, cases such as #51317<#51317> are heavily debated just because of making sure the compiler and the runtime handles the case in a way that the user does not have to care or will be safe from their own actions.
Like, I see all the time arguments from both ways.
* Go has to be simple and make sure the user is safe
* We do not have to protect the user from making Country(5)
always depending on which side of the argument you're on.
If Go strives to be safer to use, then you should prevent a user from having such weird cases such as that. And there are cases and have seen or I'm not sure even have written myself I won't put myself in another "im the best programmer" bag, because it was "easy" or comfortable at the time to do so and because the programmer just didn't think too much about it.
Where an pseudo-enumeration was written like that in an unsafe manner.
—
Reply to this email directly, view it on GitHub<#19814 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAKTSGKGQWF2HSQSUY2UDMTYOOOPTAVCNFSM4DGA7O32U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBZGA4DSMZVGEZA>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
[like] WeiYaPeng(Jim) reacted to your message:
…________________________________
From: Apostolis A. ***@***.***>
Sent: Sunday, January 14, 2024 9:21:58 AM
To: golang/go ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [golang/go] proposal: spec: add typed enum support (#19814)
good autocomplete features
Actually, the IDE I use provides good autocomplete suggestions based on the type of the constants.
To me if feels like creating a new lib into the stdlib. As in, it's something new, if somebody doesn't use it, how would it break compatibility?
A new keyword breaks compatibility, as existing code using an identifier with the same name as the keyword would no longer compile.
I may be genuinely wrong, and just curious, how would type MyEnum enum {} break compatibility? Where you would actually use this as a golang programmer in a way that would break compatibility? It's not like you would use this format as a variable format, you cannot. Maybe some other way that can break compatibility?
I don't know, let me know what you think if it would actually break compatibility
—
Reply to this email directly, view it on GitHub<#19814 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAKTSGP26ICH5CVX6IHQPP3YOOPTLAVCNFSM4DGA7O32U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBZGA4DSNJVGU4A>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
[like] WeiYaPeng(Jim) reacted to your message:
…________________________________
From: Apostolis A. ***@***.***>
Sent: Sunday, January 14, 2024 9:27:53 AM
To: golang/go ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [golang/go] proposal: spec: add typed enum support (#19814)
The intention of a good compiler is supposed to prevent even the case of such cases in the first place.
This may be the intention of a proof assistant, but not of a general-purpose programming language compiler. The latter must strike a balance between often contradictory requirements. It should aim to guard against common accidental mistakes, not against deliberate self-sabotage. Otherwise, the compiler wouldn't compile statements like os.RemoveAll("*") or similar.
I see what you mean, I think you're presenting though two completely separate cases:
os.RemoveAll("*") is code that does something, you should not prevent the programmer from executing code as a compiler at least imo.
Country(5) is syntax which can just be improved massively by just having enums
—
Reply to this email directly, view it on GitHub<#19814 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAKTSGP7HGQGX4JVSP5Z22TYOOQJTAVCNFSM4DGA7O32U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBZGA4DSNZQG42Q>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
"You" refers to the Go team at Google If you are afraid of having to change thing in the standard lib thus casing problems, just dont. I'm happy if I have acces to enums. You also said that there where no good reasons to add enums, but there is and here are some
Arguments why GO shouldent have enums
I still think GO is a wounderful language its a solid 8/10 but with enums is easily a 9/10 I don't even know anything else you can improve. The package manager is the best I've ever used, the code is easy to read and write and the VSCode extension is 12/10. This is how I'd think enums should look like package main
import "fmt"
type Fruit enum {
Apple int = 20
Orange
Grape string = "Grape!"
}
func main() {
Enum := Fruit.Apple
if Enum == Fruit.Apple || Enum == 20 {
fmt.Println("Apples")
}
switch Enum {
case: 20, Fruit.Apple:
fmt.Println("Apple")
case: "Grape!", Fruit.Grape:
fmt.Println(Fruit.Grape)
default:
fmt.Println("No fruit here!")
}
} |
@HaraldWik There are 207 comments on this issue (208 now, I guess) which is enough to show clearly that there is no consensus on precisely what enums should look like in Go. Different people have different concerns. Also, there is nearly 15 years of use of Go showing that people are able to write code without enums in the language. So enums might be nice, but they don't seem to be necessary. I am not aware of any significant kinds of code that can't be written today but that could be written if we had enums. That said I'm personally not opposed to adding language features that make it easier to write correct Go code. But we need a clear consensus on the new feature and how people would use it. We don't have that. |
@ianlancetaylor |
@HaraldWik it's not just you. Not having enums is a huge hindrance on the language. Pretty sad we still don't have them at this point. Sure, Go has worked so far without them, but it's all a bunch of workarounds and less safe code. |
Other way to look at it is.. just because there is no std ENUM support in
go, now people have come up with many other tools to generate ENUM and
otherwise much more boilerplate code for the needs. Every programming
construct just helps the developer to write fewer lines of code, or enhance
the entire safety , readability and maintainability of the code.
There are go usage for 15 years doesn't qualify as the reason to say no for
ENUM. For example, look at the variable declaration format that go
supports, the operator "*:=", Go could have existed without this operator.
this just helped the developer to write more safer code at fewer lines. If
go wouldnt have introduced this, still developers would have made the apps
with other constructs.*
*And also saying there is no standard meaning for ENUM. Within all the
meaning that everyone posting i think the common things goes like this:*
*ENUM* (short for "enumeration") is a data type consisting of a set of
named values called elements or members. The primary purpose of an ENUM is
to assign symbolic names to a set of values to make the code more readable
and *to restrict the possible values that a variable can take*.
Key Characteristics of ENUMs
1.
*Named Values*:
- ENUMs consist of named values which are usually related and represent
a finite set of possible options.
2.
*Type Safety*:
- ENUMs ensure that variables can only take one of the predefined
values, providing a layer of type safety.
3.
*Readability*:
- Using ENUMs makes the code more readable by replacing numeric or
string literals with meaningful names.
4.
*Self-Documenting*:
- ENUMs serve as documentation for the allowable values a variable can
take.
*And definitely, there can be more to it, with respect to various use case.
With the current cost model, though we are achieving the functional
behaviour that we needed, we are losing some of the aspects.*
…On Wed, Jun 5, 2024 at 8:13 AM Rob Prentiss ***@***.***> wrote:
@HaraldWik <https://github.com/HaraldWik> it's not just you. Not having
enums is a huge hindrance on the language. Pretty sad we still don't have
them at this point.
Sure, Go has worked so far without them, but it's all a bunch of
workarounds and less safe code.
—
Reply to this email directly, view it on GitHub
<#19814 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AE6FCSQETMLPBEWUOVOKG7TZFZ3MTAVCNFSM4DGA7O32U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMJUHA3TKMRVHE2Q>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@ysaakpr Why anyone still uses GO even tho thers no enums is because the language is so good in generall, its just that GO whould greatly improve if there were enums in the language, and what I don't understand is why the GO team don't want to implement enums because thers no need for them, I mean C is a "simple" language with few keywords and the C developers added enums, but GO can't because of complexity or some thing else, I mean if they whould just make it into a stdlib thing it whould be fine because then they don't need to touch the compiler, and if you are woundering why I don't just make a enum package for myself is that anyone that want to use my other packages whould need to have that package too, so the point is that I whould just like there to be a standard enum that the community chould use that is supported by the GO team. |
C has neither interfaces nor I'll also note that C enums are pretty minimalistic in what you can do with them, so in a way, they actually underscore the point Ian is making, that no one can quite agree on what to expect from them. Your comment above also demonstrates that, given that it is absolutely not clear how that is supposed to work - I don't think I've ever seen it proposed that you could mix integers, strings and completely valueless declarations in an enum. So, you seem to understand something completely different as "the bare minimum" you'd expect from enums than anyone I know. This issue discussion is long and it contains a lot of reasonable design space and yet, there is no consensus about which of those myriads of features are critical and which are not, once you get beyond what you can do with |
The C enum comment was just an exampel that languages that are "simple" have enums and I'm happy that GO has sum/union types and interfaces but thats not the point, the point is that C has enums and GO doesent, even PHP has enums (sorta) The second point you brought up I think is fair, I think the best way to resolve that is to make a vote where you can pick from diffrent enum styles. "once you get beyond what you can do with const and iota. That's the reason why they are not added. Whether the added complexity is worth the benefit is something we can only really discuss once we have an idea of what the added complexity and the benefit is." <- As stated previosly I think they chould make it into a standard lib, that doesent add more complexity to the compiler as it seems that they don't want to make drastic changes because they don't want it to become unstable. |
If the team does not listen to the community's (suggestions/proposals), I think this is no longer an open source community |
This comment was marked as off-topic.
This comment was marked as off-topic.
I think you missed my point. You can't divide the world of programming languages into "simple" and "non-simple" ones and then say "being simple is not mutually exclusive with having enums". Being simple is, primarily, a function of orthogonality. C made some choices and Go made some choices and based on those choices, enums do not fit as orthogonally into Go as they fit into C. You can't just take a feature from one language and port it to Go and expect it to fit in well. Because the rest of the language you are taking it from is different from the rest of Go and thus fits things differently.
Go has never been developed by vote. Personally, I like that very much.
I have no idea how you imagine this working, it does not make a lot of sense to me. Feel free to write them as a 3rd party package and point at that, which should demonstrate the concept. Yes, I know you think they are only helpful if they are in the standard library, but the point is that once code exists, it's easier to discuss whether or not that code should make it into the standard library.
This claim comes up occasionally and in my opinion, this post by @ianlancetaylor is still the definitive summary of that debate. |
|
Had a few spare minutes and had a little fun: package main
import "fmt"
// EnumValue represents a value in an enum with a generic type T and a discriminant type
type EnumValue[T comparable, discriminant any] struct {
value T
}
func (e EnumValue[T, discriminant]) Unwrap() T {
return e.value
}
func (e EnumValue[T, discriminant]) Value() any {
return e.value
}
func (e EnumValue[T, discriminant]) discriminant() discriminant {
var d discriminant
return d
}
// Enum interface for generic enum handling
type Enum[discriminant any] interface {
Values() []EnumMember[discriminant]
Value(int) EnumMember[discriminant]
Index(EnumMember[discriminant]) int
Len() int
}
type EnumMember[T any] interface {
discriminant() T
Value() any
}
type enum[discriminant any] struct {
values []EnumMember[discriminant]
}
func (e *enum[discriminant]) Values() []EnumMember[discriminant] {
return e.values
}
func (e *enum[discriminant]) Value(i int) EnumMember[discriminant] {
return e.values[i]
}
func (e *enum[discriminant]) Index(v EnumMember[discriminant]) int {
for i, val := range e.values {
if val == v {
return i
}
}
return -1
}
func (e *enum[discriminant]) Len() int {
return len(e.values)
}
func (e *enum[discriminant]) AddValue(v EnumMember[discriminant]) int {
l := len(e.values)
e.values = append(e.values, v)
return l
}
func NewEnum[discriminant any](values ...EnumMember[discriminant]) Enum[discriminant] {
return &enum[discriminant]{values}
}
func PrintEnum[T any](e Enum[T]) {
for _, v := range e.Values() {
fmt.Println(v.Value())
}
}
func main() {
// basically the "fruits" type is a unique identifier of the enum that can be understood as being the enum name.
type fruits struct{}
type Fruit = EnumMember[fruits]
var Apple = EnumValue[string, fruits]{value: "apple"}
var Orange = EnumValue[string, fruits]{value: "orange"}
var LeeT = EnumValue[int, fruits]{value: 1337}
var Fruits = NewEnum[fruits](Apple, Orange, LeeT)
PrintMember := func(ev Fruit) {
fmt.Println("\nPrint the enum value passed as argument: ")
fmt.Println(ev.Value())
}
PrintEnum(Fruits)
PrintMember(Apple)
fmt.Printf("\nAre %v and %v equal? %v \n", Apple, Orange, Apple == Orange)
fmt.Printf("Are %v and %v equal? %v \n", LeeT, LeeT, LeeT == LeeT)
}
That doesn't handle nilability yet but that's a whole other issue. There are several other ways to achieve similar still. My main issues would be how to deal with nil and also, how to add basic types as enum cases which requires union types implemented as interfaces. I do have some ideas but that needs some exploration. Edit: To be clear, this is just tinkering around enums and heterogeneous collections. Could also only support homogeneously typed enums in which case the equal operator would be sufficient. |
I wrote a little tool to collect enumerated members defined as structures through generalizations and one-time reflections, and provide generic methods by embedding an anonymous structure. https://github.com/ludaplus/enums type innerPostType struct {
enums.Element[*innerPostType]
CommentEnabled bool
}
var PostType = enums.Of(&struct {
enums.Enum[*innerPostType]
Unknown,
Post,
Page,
Note *innerPostType
}{
enums.Enum[*innerPostType]{},
&innerPostType{
CommentEnabled: false,
},
&innerPostType{
CommentEnabled: true,
},
&innerPostType{
CommentEnabled: false,
},
&innerPostType{
CommentEnabled: false,
},
})
func main() {
fmt.Println(PostType.Unknown.Name())
fmt.Println(PostType.Post.CommentEnabled)
fmt.Println(PostType.ValueOf("Page").Ordinal())
fmt.Println(PostType.ValueOf("Note") == PostType.Note)
for _, postType := range PostType.Values() {
fmt.Println(postType.CommentEnabled())
}
} |
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
How it might look with language support
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.
SearchRequest(99)
orSearchRequest("MOBILEAPP")
. Current workarounds include making an unexported type with options, but that often makes the resulting code harder to use / document.Things to Consider
enum
on top of the type system, I don't believe this should require special casing. If someone wantsnil
to be valid, then the enum should be defined as a pointer.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.
The text was updated successfully, but these errors were encountered: