Skip to content
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

Enums, value classes and beyond #183

Open
lexaknyazev opened this issue Jan 17, 2019 · 2 comments
Open

Enums, value classes and beyond #183

lexaknyazev opened this issue Jan 17, 2019 · 2 comments

Comments

@lexaknyazev
Copy link

Hi. These topics have been brought up several times in the language and SDK repos, however I'd like to show a particular use case that may help with designing future language features.

While writing parsers or generators for binary file formats, the following code evolution usually happens. Out of simplicity, this is how it starts:

// type of data
const text = 7;
const image = 8;

final type = readByte();
switch (type) {
  case text:
    //readText();
  case image:
    //readImage();
  default:
    // throw
}

Then, either the number of consts grows, or another field emerges. So, to keep values organized, static consts within abstract classes could be used. This approach scopes values (thus preventing certain errors), but doesn't provide full type safety, since values are just integers.

abstract class DataType {
  static const int text = 7;
  static const int image = 8;
}

abstract class CompressionType {
  static const int none = 0;
  static const int deflate = 2;
}

final type = readByte();
final compression = readByte();
// ...

To achieve type safety, much more boilerplate is needed:

class DataType {
  final int value;

  const DataType._(this.value);

  factory DataType(int value) {
    switch(value) {
      case _text:
        return text;
      case _image:
        return image;
      default:
        return null; // or throw ArgumentError
    }
  }

  static const int _text = 7;
  static const int _image = 8;

  static const DataType text = DataType._(_text);
  static const DataType image = DataType._(_image);
}

final type = DataType(readByte()); // instance of DataType
// ...

The next step usually involves adding string names to these values and overriding toString for a better debugging.

class DataType {
  final int value;
  final String name;

  const DataType._(this.value, this.name);

  factory DataType(int value) {
    switch(value) {
      case _text:
        return text;
      case _image:
        return image;
      default:
        return null; // or throw ArgumentError
    }
  }

  static const int _text = 7;
  static const int _image = 8;

  static const DataType text = DataType._(_text, 'text');
  static const DataType image = DataType._(_image, 'image');

  @override
  String toString() => '$value ($name)';
}

Could future language development reduce the amount of boilerplate here or it would be more appropriate to go with codegen?

@jamesderlin
Copy link

Is there a reason why Dart enum doesn't allow specifying explicit values like most other C-like languages? (This seems to be the closest issue I can find about it.) There have been a number of people asking how to work around that limitation on StackOverflow:

const int global/static variables typically seem to be used instead, but as noted earlier, that loses type safety, and it loses exhaustiveness checks for switch statements.

@lrhn
Copy link
Member

lrhn commented Jan 26, 2022

The currently-being-worked-on enhanced enum feature may help. It would allow you to write:

enum DataType {
  text._(7), 
  image._(8);

  final int value;
  
  const DataType._(this.value);

  factory DataType(int value) {
    for (var e in values) {
      if (e.value == value) return e;
    }
    // You can't return `null` from a constructor in null safe code.
    throw ArgumentError.value(value, "value", "Not the value of a DataType");
  }

  @override
  String toString() => '$value ($name)';
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants