-
Notifications
You must be signed in to change notification settings - Fork 5
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
Enum with associated values #30
Comments
How will we encode these enumerators, I assume we encode each using a struct with a type ID, and enumerators without associated values, encode like an empty struct of the given type. |
I had thought it'd be something like:
for Foo we'd encode We really don't even need type-IDs here, since we know exactly what type will be passed in. |
One question I see is whether we should allow tags here...
I think allowing optionals is totally fine, but tags, I don't know. I think it's fine to not support them, making enumerators effectively like compact structs. |
I think allowing optional here is more flexible, one option would be to allow using
|
I lean towards having only compact enums (and hence no compact qualifier) because "regular" enums without associated values should (obviously) be compact. If we allowed tags in associated values, then
|
I agree, supporting tags here seems overkill. Note that there's nothing wrong with enumerators holding non-compact structs, so they can still have flexibility in the underlying data:
|
I feel that tags are important because they allow applications to evolve, is true that you can use a struct with tags but you have to anticipate the need, I think that defaulting to evolvability and letting the user opt-in |
The first thing we need in terms of extensibility for enums with associated values is to support unchecked enums with associated values. This way, if you don't like an existing associated value, you can introduce a new discriminant with the desired value. Then we can piggy-back on this extensibility to add tags to the enum's associated values. Proposal
For example: compact enum Location {
Unknown
Anonymous
Known(coordinates: Coordinates)
} Here, Unknown is encoded as 0 (as a varint32), and Known is encoded as 2 (varint32) followed by the coordinates. The Coordinates struct is encoded like a compact struct holding Coordinates. Without enum Location {
Unknown(tag(1) name: string?) // wire compatible
Anonymous
Known(coordinates: Coordinates)
}
unchecked enum Location {
Unknown
Anonymous
} compact = tags not allowed + smaller encoding size (1 byte less: not much) Every enumerator is encoded as: For example, Then, you could extend it with: unchecked enum Location {
Unknown
Anonymous
Known
} and later: unchecked enum Location {
Unknown(tag(1) message: string?)
Anonymous
Known
KnownWithCoordinates(coordinates: Coordinates)
} During decoding:
|
Tags are so powerful with structs because they're 'extensible by default'. If you just type Having enums be 'un extensible by default' makes the feature partially useless in practice. Few users will have the foresight (or even knowledge) to preemptively mark their enums More likely they'll only write So, if we go with this approach, I think we'd have to switch the behavior of enums with variants: This conclusion is a little unfortunate though, because while "unchecked by default" is ideal for enums with variants, "checked by default" makes more sense for 'normal' enumerators... |
I updated the proposal. It's now extensible by default as far as the associated values are concerned. |
|
From this proposal, am I correct that you could have a Also, I assume that all the parts about associated values are Slice2 specific, correct? Just to make sure! |
The more I think about this, the more it feels like we're talking about 2 completely separate types. 'normal' enums:
enum with associated types:
Should we maybe just make them two separate types?
I think splitting them would make some of the terminology less confusing. It also makes it easier to describe things to users: |
This analysis is correct, and my proposal does include two distinct syntaxes that use the same
Swift does the same, with a single keyword (enum). See https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/ AFAIK all programming languages with enum-with-associated-values (aka tagged union, discriminated union...) call this construct 'enum'; it would be confusing to pick a different name in Slice. |
I'll admit it isn't very compelling, but C++ does have different names for these constructs. |
It's interesting that we arrived at opposite conclusions from the same information! To me, it would be confusing to use the same name because for developers from C#, Java, TypeScript, Kotlin, C++, etc., Many languages use Either way, not a pressing discussion to have. |
For enum with associated values in C#, see also: https://github.com/domn1995/dunet |
In languages like C# that don't have native support for these,
This proposal says the deciding factor will be "is there an underlying type?"
This produces a bad mapping for the most common kind of enum:
These are the cleanest looking enums, and the one that users will initially reach for. But will get treated as enums with associated values, and mapped 'poorly' in many languages: In my opinion, deciding which mapping to use should be based on It's worth remembering most languages do not support associated values. C# doesn't, Kotlin doesn't, Typescript doesn't, Python doesn't, Go doesn't even have enums, let alone with associated values. Ensuring that most languages have natural, expected mappings out-of-the-box is more important than optimizing the syntax of enums for the 2 languages (Rust and Swift) that can properly use this new feature IMO. P.S. I just feel like normal enums should be the default in my gut. |
A concern I anticipate is: "future extensibility".
You are correct, but "future extensibility" is unsolvable here. With the current proposal, this is already a breaking change:
No matter which choice we make, we will be loosing one form of extensibility. At best, we can try to argue about which form of extensibility is more important, |
That's not correct. The current proposal doesn't allow you to assign a value to an enumerator when the enumeration has no underlying type. |
Parser support for enums with associated values was added in #664. |
This is a proposal to add associated values to Slice enum, similar to Swift and Rust.
See:
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
https://doc.rust-lang.org/rust-by-example/custom_types/enum.html
The syntax of Slice enums with associated values is similar to Rust/Swift:
Syntactically, it's similar to an operation with parameters and no return value.
A Slice enum where any enumerator has one or more associated values:
enum WebEvent : byte
)In C#, WebEvent and its enumerators would map to a small generated record hierarchy:
Usage: https://dotnetfiddle.net/LH5C6q
This also works well with switch case and switch expression, since we can match on the type (like WebEvent.KeyPress).
The text was updated successfully, but these errors were encountered: