Skip to content

Allow enum classes to have members. #1570

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

Closed
lrhn opened this issue Apr 9, 2021 · 7 comments
Closed

Allow enum classes to have members. #1570

lrhn opened this issue Apr 9, 2021 · 7 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@lrhn
Copy link
Member

lrhn commented Apr 9, 2021

Currently enum declarations only allow you to declare the enum values themselves.
With extension methods, you can then add methods on the enum anyway, and people are doing that.
Since enums are non-subclassable, there is no distinction between non-virtual extension methods and proper methods, so we are effectively giving people the ability to add methods to enum types, but without the convenience of being able to declare them in-line. (Also, you have to import the declaration to use the extension members, but you can use instance members directly on a returned object without importing its type).

We can even allow instance variables, but since all instances are const-created, the instance variable must be final and const-initializable, and there is no way to write a constructor, so the variable would always have the same value. We might as well disallow instance variables for now.

We currently cannot add static methods with extension methods (but we might, #723).
If we allow methods on enums, we should also allow static methods. People would be able to add their own parse or tryParse methods. There are lots of requests for that. No generative constructors allowed, factory constructors are fine (we'll just have to make sure the actual generative constructor used to create the instances has a fresh name).

(Alternatively, allow a single unnamed constructor too, with arguments passed in the enum elements as:

enum Foo {
  foo(1), bar(2), baz(42);
  final int value;
  Foo(this.value);
}

which would be rewritten to something like:

class Foo extends Enum {
  static const foo = Foo(0, "Foo.foo", 1);
  static const bar = Foo(1, "Foo.bar", 2);
  static const baz = Foo(2, "Foo.baz", 42);
  static const List<Foo> = [foo, bar, baz];
  final int value;
  Foo(int $index, String $name, this.value) : super._internal($index, $name);
}

with a superclass declared something like:

abstract class Enum<T> { 
  final int index; 
  final String _name; 
  const Enum._internal(this.index, this._name); 
  String toString() => _name;
}

This would allow users to attach values to enums, and access them through their own getters/methods as well.)

(Proposal: https://github.com/dart-lang/language/blob/master/working/0158%20-%20Enhanced%20Enum/proposal.md)

@lrhn lrhn added the feature Proposed language feature that solves one or more problems label Apr 9, 2021
@lrhn
Copy link
Member Author

lrhn commented Apr 9, 2021

I'm not proposing any automatic methods, just giving you the ability to write them yourself.

@lrhn
Copy link
Member Author

lrhn commented Apr 10, 2021

Whether you want or need a fromString is enum dependent. It's not something I want to put on all enums by default.
(And that's without getting into whether it should be case insensitive or trim whitespace).

Users might complain if you do it badly, but they'll complain to you. I don't want to make them complain to the language for not doing the particular thing they want as the default. It's sometimes better to do nothing than to do something almost, but not entirely, completely unlike what you really need.

If you want to automatically add template methods, I think a feature to do that would be fine, that's what meta programming is about. I just want to make it possible to have methods at all, and leave it up to other features, or authors themselves, which ones to add.

(If you want help with the fromString, I give you:

T enumFromString<T extends Enum>(String name, List<T> values) {  // Remove `extends Enum` if we don't have that class.
  for (var value in values) {
    var string = value.toString();
    if (string.endsWith(name) && string.codeUnitAt(string.length - name.length - 1) == 0x2e /*.*/) {
      return value;
    }
  }
  throw ArgumentError.value(name, "name", "No enum value with that name");
}

Then your fromString can just be static Foo fromString(String name) => enumFromString(name, Foo.values);.)

@munificent
Copy link
Member

Yes, I would love for us to do this. I rarely get to use actual Dart enums because I invariably want some other operations or state in the enum cases. (This is somewhat less true now that we have extension methods.) But doing the enum pattern instead of a real enum means sacrificing exhaustiveness checking in switches, which sucks.

Members on enums would let you have all of those nice affordances and keep the exhaustiveness checking. Also, we could let enums implement interfaces, which would be eminently handy and isn't covered by extensions.

@PiN73
Copy link

PiN73 commented Apr 24, 2021

Related: #349

@Levi-Lesches
Copy link

Now that the Enum class has been released, would the methods above work? There is fierce discussion about this at #158

@mit-mit
Copy link
Member

mit-mit commented Sep 27, 2021

Closing as a duplicate of #158

@creativecreatorormaybenot
Copy link
Contributor

@mit-mit I think you forgot to hit the "Close" button 🙈

@lrhn lrhn closed this as completed Sep 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

6 participants