Skip to content

extension enum #4329

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

Open
kirara-shiroyoru opened this issue Apr 16, 2025 · 9 comments
Open

extension enum #4329

kirara-shiroyoru opened this issue Apr 16, 2025 · 9 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@kirara-shiroyoru
Copy link

When I need a set options(typically for switch), no run-time type check required, enum in dart is an over-kill, I would use extension type and static const variables instead.

Just like using the same syntax in a class, it is verbose, therefore I expect a feature similar to enum as extension type. Though not having it would be fine, having it would be great.

This is how it would look like right now

extension type const SudoEnum(int i){
  static const option1 = SudoEnum(1);
  static const option2 = SudoEnum(2);
  static const option3 = SudoEnum(3);
}

And this is what I expect it to be

//For the constructor representing underlying structure,
//extension enum is always const, 
//I think const keyword should be omitted.
extension enum SudoEnum(int i){
  option1(1), option2(2), option3(3);
}
@kirara-shiroyoru kirara-shiroyoru added the feature Proposed language feature that solves one or more problems label Apr 16, 2025
@Wdestroier
Copy link

Wdestroier commented Apr 18, 2025

With primary constructors you can write

enum SudoEnum(int i) { option1(1), option2(2), option3(3) }

EDIT: for reference see #2364.

@kirara-shiroyoru
Copy link
Author

With primary constructors you can write

enum SudoEnum(int i) { option1(1), option2(2), option3(3) }

EDIT: for reference see #2364.

Maybe I am strange, but I somehow care about creating an extra class, and that is why I use extension types.

@lrhn
Copy link
Member

lrhn commented Apr 18, 2025

Nothing prevents you from doing 4 as SudoEnum, so the enum isn't closed. Which means it's not an enum.

Trying to prevent that cast won't work, there'll always be a way, as long as it's an extension type.

@lrhn
Copy link
Member

lrhn commented Apr 22, 2025

And to be clear: You are strange. So am I. I completely get not wanting ... well, not the class, but the instance of the class.

The language just doesn't support that. The things you want from an enum are things that are inherently based on controlling identity of the values, and extension types delegate the identity, and control of instance creation, to the representation type.

The feature you want is not in the extension type feature.

@kirara-shiroyoru
Copy link
Author

And to be clear: You are strange. So am I. I completely get not wanting ... well, not the class, but the instance of the class.

The language just doesn't support that. The things you want from an enum are things that are inherently based on controlling identity of the values, and extension types delegate the identity, and control of instance creation, to the representation type.

The feature you want is not in the extension type feature.

The thing I want is just some integer that cannot be miswritten in code in wrong place.

The function or variable should not use the wrong integer, and the integer sould not be used in wrong way, so use extension type.

But simply creating a value manually everywhere is definitely a bad idea since I can remember or mistyped wrong value, so they should be predeclared in right scope with a readable name with meaning.

Declaring those integer is wordy. Enum looks simple, but why should I create a runtime class that exist just for a simple option?

I am just finding some easier way to declare all these static const.

@lrhn
Copy link
Member

lrhn commented Apr 22, 2025

As a convenience and help from the type system in not using incorrect values, an extension type with a fixed set of constants will work perfectly fine.
It doesn't give the closure guarantees of an enum, which is why an extension enum type won't be a language feature. It wouldn't provide the other things an enum does, like exhaustiveness.

But the enum value syntax is awesome.
Maybe we could consider allowing any class-like type declaration to declare constants.
Maybe

extension type Foo(Bar _) {
  enum foo1(bar1), foo2(bar2);
}

It'll just be shorthand for static const foo1 = Foo(bar1); and ...2...2....

Could be used in plain classes and mixins too.

@mmcdon20
Copy link

mmcdon20 commented Apr 23, 2025

But the enum value syntax is awesome. Maybe we could consider allowing any class-like type declaration to declare constants. Maybe

extension type Foo(Bar _) {
  enum foo1(bar1), foo2(bar2);
}

It'll just be shorthand for static const foo1 = Foo(bar1); and ...2...2....

Could be used in plain classes and mixins too.

It's an interesting idea, but I think using the enum keyword would be confusing if you also eventually allow static nested enums/classes/mixins/etc. Also I don't think the feature would work with mixins since mixins don't have constructors, could also rule out abstract and sealed classes also since you can't instantiate those either.

But if there was such a feature, I would expect there to be no keyword, just a requirement that it come before any other member (same restriction enums currently have), and that it can only be used with const constructors.

extension type const Foo(Bar _) {
  foo1(bar1), foo2(bar2); // must come before other members and
                          // only allowed with const constructors
}

Alternatively, foo1(bar1) could be shorthand for foo1 = Foo(bar1), and can be used with current keywords used to declare variables:

extension type const Foo(Bar _) {
  static const foo1(bar1), foo2(bar2);  
  // static const foo1 = Foo(bar1), foo2 = Foo(bar2);
}

class Baz {
  Baz(this.x, this.y);
  Baz.named(this.x, this.y);
  int x, y;

  static Baz a(1, 2), b.named(3, 4); 
  // static Baz a = Baz(1, 2), b = Baz.named(3, 4);
  static final c(5, 6), d(7, 8);
  // static final c = Baz(5, 6), d = Baz(7, 8);
}

@lrhn
Copy link
Member

lrhn commented Apr 23, 2025

Nested enum declarations would conflict. Sadly a good point, so just the enum keyword won't work.

Mixins, abstract and sealed classes could work with const factory constructors (if we haven't allowed mixins to have factory constructors yet, we should).

Just foo(bar); is a valid untyped abstract function declaration, so some keyword or different syntax is needed. It would work for static const, or just final, but not for plain mutable instance variables. (Not a big loss, you could write var in front, but an inconsistency.)
Also doesn't work for local variables, and even with var in front of looks like an object pattern.
And on a parameter list, it's already a function typed parameter (not that you can declare values there, but the same syntax means something completely different.)

Should just make it look like markdown or yaml 😁

class const Foo(int x) {
  - va(1)
  - vb(2)
  - vc(3)
  ;
}

Not very "Dart-ish", though. (And should * work too?)

@mmcdon20
Copy link

mmcdon20 commented Apr 23, 2025

Mixins, abstract and sealed classes could work with const factory constructors (if we haven't allowed mixins to have factory constructors yet, we should).

Fair enough regarding abstract and sealed classes, but mixins can not even declare a factory constructor.

Edit: I should note that factory constructors are not allowed in enums, so It's unclear if it's entirely necessary for such a feature to support factory constructors.

Just foo(bar); is a valid untyped abstract function declaration, so some keyword or different syntax is needed.

I'm not sure you would need a different syntax altogether to disambiguate.

  1. Most of the time the feature would likely be used in concrete types where you can't have abstract function declarations to begin with, so that leaves abstract classes, and sealed classes where the ambiguity can occur.

  2. Enums are rarely defined with only one value, and the ambiguity only happens if you are defining only one value.

  3. Most of the time you are not passing in a variable name you would be passing literal values like foo(1), or foo('hello') and not foo(bar) where bar is presumably a top-level variable.

  4. Currently in dart enum A {a,;} is valid syntax. Note the trailing comma before the semi colon. So in the rare event that there is a conflict you could say foo(bar); is an abstract function and foo(bar),; is an enum style constant.


const bar = 1;

abstract class Foo {
  foo(bar),; // trailing comma required because 
             // ambiguous with abstract method
  
  // alternatively, these should be unambiguous also
  // foo(1);
  // foo.new(bar);

  const Foo._(this.value);
  const factory Foo(int value) = _Foo;

  final int value;
}

class _Foo extends Foo {
  const _Foo(super.value) : super._();
}
const bar = 1;

class Foo {
  foo(bar); // no trailing comma required because
            // concrete classes cannot have abstract methods
  const Foo(this.value);
  final int value;
}

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

4 participants