-
Notifications
You must be signed in to change notification settings - Fork 205
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
Allow enum classes to have members #158
Comments
Just FYI, a similar topic has been on the radar a while ago, e.g., here: #83 (comment). |
Thanks for letting me know, if this thread is still of use to anyone, I would propose a syntax like so. Not sure how Darty it is. enum Time<String> {
hour = "1h",
day = "1d",
week = "1w",
month = "1m",
year = "1y"
} |
In Java, any enum instance could have its own field variables, methods, and could even implement abstract methods. The values could be passed in to the enum constructor. public enum MyEnum {
COLD(0),
WARM(24),
HOT(37);
private final int temperature;
private MyEnum(int temperature) {
this.temperature = temperature;
}
} Any reason why a single template variable would be preferable to that sort of solution? Note: I don't know how Dart works, just asking 😄 |
The bulk of my experience is in TypeScript and they allow a similar thing to what you describe. I personally have no need to have enums return methods, but I certainly have nothing against it! |
@Zhuinden If I recall correctly, this was debated in pre-Dart 1 days and some people were opting for simplicity and against Java style enums. |
I personally think that an enum should be able to store An alternative is to add a method to enums that outputs the index or the string. So Also, enums really should allow leading numbers, but that may be a different issue. myEnum {
1m: Duration(minutes: 1),
15m: Duration(minutes: 15),
1h: Duration(hours: 1),
} This would have saved me an extraordinary amount of time the other day especially if myEnum had methods like |
Shame, they are missing out, although now with Kotlin I'd favor But storing simple const values? Totally reasonable I think. You shouldn't have to build a map for each associated value. |
I have noticed that Dart seems to value simplicity over sugar. I personally value sugar over simplicity since sugar is optional. But I get where they are coming from. There is a lot of value in simplicity. |
I'm currently using classes full of |
Wouldn't it be simple to just make enum extendable, and allow the basic usage to continue to allow for backwards compatibility but then give us the ability to add our own submethods like |
Maybe something more simillar to current dart syntax but much more practical: enum Time(String this.yourCustomValue) {
hour("1h"),
day("1d"),
week("1w"),
month("1m"),
year("1y");
final String yourCustomValue;
} Define variables by this way gives you an opportunity to declare more than one value to enums :) |
Just learned about how swift handles enums and I find it to be a good solution // Enums can optionally be of a specific type or on their own.
// They can contain methods like classes.
enum Suit {
case spades, hearts, diamonds, clubs
func getIcon() -> String {
switch self {
case .spades: return "♤"
case .hearts: return "♡"
case .diamonds: return "♢"
case .clubs: return "♧"
}
}
}
// Enum values allow short hand syntax, no need to type the enum type
// when the variable is explicitly declared
var suitValue: Suit = .hearts
// String enums can have direct raw value assignments
// or their raw values will be derived from the Enum field
enum BookName: String {
case john
case luke = "Luke"
}
print("Name: \(BookName.john.rawValue)") The example goes on to include other unique features of Swift enums which I don't think we should consider for Dart. For dart I would rewrite the two examples like so: enum Suit<IconData> {
spades = Icons.spade,
hearts = Icons.heart,
diamonds = Icons.diamond,
clubs = Icons.club,
}
enum BookName<String> {
john,
luke = "Luke",
} I know these start to blur the line between enums, structs, and maps, but it's food for thought. |
@lukepighetti How do you want to declare 2 or more values to the enum? Eg. for |
Gradient is kinda heavyweight but I would imagine something like this (from Flutter) enum AppGradient<Gradient> {
murica: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: <Color>[Colors.red, Colors.white, Colors.blue],
stops: <double>[0.0, 0.5, 1.0],
),
canuckistan: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: <Color>[Colors.red, Colors.white, Colors.red],
stops: <double>[0.0, 0.5, 1.0],
),
} but as you can see it gets closer to a static class, i'm not sure i would use it this way. My desire to have a enum map to something like a string is so that it's easy to build an enum from something like a json response |
II constantly find myself using the following helper methods when interfacing with the outside world: String enumToString(o) => o.toString().split('.').last;
T enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split('.').last == value,
orElse: () => null);
} I also favor enums over static classes like |
Wouldn't it be easier just user Maps? Map<String, String> values = {
"hour": "1h",
"day": "1d",
"week": "1w",
"month": "1m",
"year": "1y"
};
main() {
print(values['day']); // 1d
print(values.keys); // (hour, day, week, month, year)
print(values.values); // (1h, 1d, 1w, 1m, 1y)
} |
@kgbsmurf it is safer and more clear to use enums. if in your example day would not exist, you would not get any value. If you use an enum you know exactly what values exist and which not. (And also it is more nice to have auto-completion, so actually the same reason) |
Good point. Thank you. |
Maps do not work with static analysis like they do in a language like typescript (iirc) |
Yea enums provide more type safety than maps. |
I had the same issue while back, I think it's a much needed feature. I've developed a helper class library which might help few people having same issue Vnum Has support for enum values, extensions, serialization, comparison, etc.
and use it as below:
|
Agreed! I'm a huge fan of another aspect of Swift's enums that hasn't been mentioned: associated values. enum LoadingState {
case loading
case loaded(Data)
}
switch loadingState {
case .loading:
print("Still loading...")
case .loaded(let data):
print("Loaded data: \(data)") // access to `data` as non-optional strongly-typed `Data`
} Enums with associated values are an excellent solution for eliminating invalid state and better describing real-world scenarios concisely. I'm new to Dart, but this is a language feature I'd love to see! I'm not sure if this is feasible, or what the Dart syntax would look like. Any ideas? |
See #546 for some relevant context. |
If dart has this feature, it would better describe the states of bloc. Currently, we are use classes to describe it. It not really concise.
|
Hi, I do prefer to have an enum that placing a values of string or other datatypes just like typescript. but for workaround I just like to share my approach to this:
but I do really hope to have an enum that holds specific values in dart. thanks! |
Is it possible to also add
Expected output
At the moment I have to define an extension for each enum class because defining an extension directly on the enum type is not supported. |
That does not make much sense since Color.green return a color object, which has nothing to do with Enum. So I don't how they can tie it together like this |
It was an example, you can easily replace Color with Cat:
Expected output |
This issue thread is going in circles. We'll all make more progress here if we slow down, spend more time reading the previous comments, and more time editing down our own comments to be concise and build on what's been previously said. @JohnGalt1717, you have written 3,039 words in the past five hours. I know you aren't editing them carefully to make it as easy as possible for everyone else to read them because I can see an increased number of typos. Slow down. The language is not designed by filibuster and you are not persuading anyone by exhausting them. To your specific points, please see my comment here. C# does not limit enum values to any combination or permutation of its defined values: [Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
var flags = (SuitsFlags)42;
System.Console.WriteLine(flags); C# enum values have always been inferior to Java enums when it comes to safety. The language provides no guarantee that a value of some enum type is actually any of its named values or a bitwise combination of them.
Dart enums are passed around by pointer like any other reference type. A variable holding an enum takes 64 bits on a 64-bit machine. If you have empirical evidence that that is vastly less efficient, I would like to see the actual data. I can't recall ever hearing anyone from the Java or Dart ecosystems indicate that enum representation was a measurable performance problem in their app. It is extremely unlikely that we could move Dart to an unboxed representation for enums even if we wanted to (which, so far, I believe no one has been convinced of). It is fundamental to Dart's object model that every type is a subtype of Object. Consider: enum Color { red, green, blue }
enum Suit { clubs, diamonds, hearts, spades}
main() {
List<Object> things = [Color.red, Suit.clubs];
print(things);
} This program prints "[Color.red, Suit.clubs]". In order to do that, the value stored in the Also, as you can see, we can't rely on the static type system to statically dispatch to each enum's C# does not have a unified object model. Each enum class is not a subtype of For example, this is not allowed in C#: class A {
public virtual object Method() { return null; }
}
class B : A {
public override Color Method() { return Color.Red; }
} If enums were subtypes of Dart does not have to auto-box because enums are true subtypes. But the implication of that is that their in memory representation must be compatible with
I would like to remind you and anyone else on the thread that the issue here is about methods on enums not enum representation. If you don't have an opinion on that, please don't comment here. It just clutters up the issue. File another issue about enum representation and we'll discuss it there. |
I've made a simple test with classes, a random number and bit flags: import 'dart:html';
import 'dart:math';
class Foo {
final int bit;
const Foo(this.bit);
}
extension Ff on Foo {
bool isSet(Foo flag) {
return (bit & flag.bit) == flag.bit;
}
}
void main() {
final rnd = Random();
final bit = rnd.nextInt(1);
final foo = Foo(bit | 2 | 8);
querySelector('#output').text = 'FooHasBFlag: ${foo.isSet(Foo(1))}, FooHasDFlag: ${foo.isSet(Foo(8))}';
}
Dart2JS transpiles all var t=C.m.p(1)|2|8
document.querySelector("#output").textContent="FooHasBFlag: "+((t&1)===1)+", FooHasDFlag: "+((t&8)===8) You can see that |
@jodinathan (and everybody else) Dart does not currently have value enums. It could, but it doesn't. Issue #50 is a request for such a feature. Value enums is also not what the current issue is about. It's about extending the We're not going to remove the current enum feature. We are likely going to extend it to make it more useful for those things that value based enums are already worse at. This issue is for discussing those extensions. If you are discussing something else, you are in the wrong place. Discussing value enums here is not helping. It won't bring you any closer to getting value-based enums. That's what issue #50 is for. |
[This comment was intended to support the notion that this is not the right issue to take a big discussion about enumerations considered as "numbers that are treated as bit sets". It illustrated that the |
@munificent Yes you can hard override enums and put invalid values into the bit mask in C#. But unless you explicitly tell the compiler you want to do something stupid, it won't let you. Dart can easily be more strict and prevent even that if you wanted. Do a simple bitshift 1 million times on Java's enums and compare it to C#. It's 7x slower as are all bitwise operators. As for your claim that Dart can do things with enums that C# can't, your example is both contrived and a good example of things you shouldn't be doing in code, and if you absolutely must, you can do so in C# by using the new keyword which doesn't exist in Dart, and create a new method on the child class and you can still call into the parent class with base to the original implementation. Thus that still isn't an example of something you can't do in C# that you can do in Dart with Enums. As for C#'s boxing and unboxing, unlike VB.net, in C# it's required to be explicit. In Dart, you're incurring the penalty by accident and the vast majority of developers aren't even aware of what they're doing, and it's this very behavior that creates an entire class of bugs in Dart with Generics that aren't required to be defined, or inferred and will default to dynamic without compilation error which shouldn't be possible and breaks a ton of stuff if a junior developer forgets to define the types on the generic they're using. (and this is all relevant to this topic, because virtually all of the use cases for methods on enums go away if you can assign values and assign |ed values to other items in the enumeration.) At any rate, I hack around the root mess of enums in dart, and since every single case here can easily be done with extension methods, and enums aren't in any critical path for me and likely won't be since Dart doesn't do server well, it's really immaterial to me and since this has turned into a religious debate instead of a functional one, I'm out. |
PS: Here's a good explainer for what I was saying about Java/Kotlin enums versus C# enums which are int based value types instead of object types on the stack. https://betterprogramming.pub/android-how-enums-can-impact-the-performance-f787ef5903dd They literally give a stand on your head approach to fix exactly what I'm referencing. It's always faster to work on the int (bits) than it is on an object. In #50 I give a solution (which is the same as C#'s solution) to the request for Members on this topic. It solves the request, and doesn't introduce even more overhead to Dart's enums. It's the bottom half of the comment/suggestion if you don't care about the rest. This could be added to the language as a abstract class with some special base functionality and then the generics functionality could be extended to allow direct definition of the enum in the generic definition which would then hide the underlying enum for which the class is based. I.e.:
Where TEnumType is restricted to the new Enum generic type restriction. (TEnumType extends Enum) And if you really wanted to be fancy, you could allow the enumeration to be defined inline:
This doesn't pollute enums, and allows developers to get all of the functionality of java (methods on enums) by choosing to get that functionality ON TOP of enum functionality, because Enumeration's generic type would extend Enum and thus allow Enumeration to have all of the root functionality that Enums have. Of course, it's a class so it's allocated on the stack, but that's fine if you want this type of functionality. And then enums can still be value types (even if they inherit from object) if the rest of my suggestion there is taken, and thus be vastly faster as a result so you get the best of both worlds. You could do this as of Dart 2.14 yourself by creating the abstract class:
Which you'd then implement like this:
You can of course add whatever other constructors you want, assign a new value to the enumeration, do equality comparisons, add methods, the whole deal. Of course if this was built into the language instead, then the Dart team could have it automatically do the values get method (if you care) and the assignment should be able to be done with just = instead of "assign" as you're able to do with C# because of explicit operator overrides. I.e. the Dart team can give syntactic sugar pretty easily for this special case, but by adding 2 features to Dart itself, then this can just be an included class that uses explicit functionality. i.e. give us explicit operator overriding that allows us to explicitly define = as an example so that we can do casting explicitly from one object type to another in code like C# does, and figure out how to define Enumeration so that the value property can be retrieved without mirrors so that the values get method can be easily created in the class without hackery or having to override it. i.e. if Dart did: class SomethingEnumeration extends Enumeration { Then we could do explicit casting at our class level this would make using Enumeration seamless in code to drop in replace a standard enum whereever we needed to add all of that function goodness. As for Values, it would be nice if there was a way to get the actual enum from the Enum class which would then make that all automatic too. |
Personally I find the current proposal confusing and I'm not a huge fan of it, but my opinion is only one of many |
The current proposal still lacks one sugar @venkatd mentioned to get the enum object from its string representation: T enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split('.').last == value, orElse: () => null);
} Such a use case is so common that even the proposal itself has an example of what one would with such enums: static MyEnum byFieldValue(String value) => values.firstWhere((e) => e._field == value); Can we possibly add static MyEnum<dynamic>? from(String _field, dynamic value) {
// Here go generated comparisons.
if (_field == "a" && value == 1) return foo;
if (_field == "b" && value == 0) return bar;
if (_field == "c" && value == 2.5) return baz;
return null;
} |
@alexeyinkin you don't need enum MyEnum {
one, two, three
}
void main() {
print(MyEnum.one.name == 'one'); // => true
print(MyEnum.values.byName('two') == MyEnum.two); // => true
final map = MyEnum.values.asNameMap();
print(map['three'] == MyEnum.three); // => true
} UPDATE: Initially stated that this was released in 2.14 which is incorrect, this is part of 2.15. |
@mraleph is it really released? DartPad with 2.14.4 breaks on this code highlighting |
@alexeyinkin you are right, it's part of 2.15 not 2.14 release, I misread the CHANGELOG. |
I just tested it in 2.15-edge and it worked flawlessly. |
related to #2006 |
This feature will be part of the next release of Dart, cf. dart-lang/sdk#47849. 🎉 |
This feature is now turned on by default at HEAD, and will be available without a flag in the upcoming beta. Work on downstream tooling support is still in progress. |
I'm glad the Dart team made enums more powerful, but I think there was a missed opportunity to make enums more useful for app state management by allowing each state to have unique instance variables. Here's an example: enum HomeScreenState {
// empty state with no instance variables
case initial
// states with instance variables that relate to the state
case loading({double progress})
case loaded({UserProfile profile, List<Document> documents})
// state with instance variables and functions
case error({Error error}) {
void displayError(BuildContext context) {
// ...
}
}
} This is how Swift does it (without the functions part), and I found it extremely useful! PS. Even though my example was geared towards app development -- I can see this being useful in stateful system. |
Dart enums are about defining a closed set of values known at compile times. Swift enums are more like algebraic datatypes in other languages where you are defining a fixed set of type cases but you may have arbitrary values stored in them and created at runtime. For the latter use case (similar to Kotlin, Scala, et al), we're working on adding sealed types and pattern matching. I think modeling algebraic datatypes using subtypes yields a more powerful expressive feature than Swift's relatively restricted enums (though I do like how terse Swift is when declaring them). |
Admin comment by @mit-mit:
This request is being investigated as a feature. We've investigating a general mechanism for adding members (fields, methods, an unnamed const constructor) to enums, for example:
For details, see the working proposal feature specification:
https://github.com/dart-lang/language/blob/master/accepted/2.17/enhanced-enums/feature-specification.md
Original feature request by @lukepighetti:
Enums in Dart require a lot of additional tooling to extract all of their utility in a production app. One of those features is mapping Enums to string. One can use a Map, but since the dart language server doesn't have the ability to report the contents of a map to IDE tools, it is not a good option for self-documenting APIs.
I believe TypeScript handles this by replacing the integer with a string when specified. Would it be possible to do the same in Dart?
Dart
TypeScript
The text was updated successfully, but these errors were encountered: