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

Support for C-Style Enums #31

Closed
rylev opened this issue Feb 18, 2018 · 9 comments · Fixed by #36
Closed

Support for C-Style Enums #31

rylev opened this issue Feb 18, 2018 · 9 comments · Fixed by #36

Comments

@rylev
Copy link
Contributor

rylev commented Feb 18, 2018

Supporting the full power of Rust enums is probably a lot of work, but C-Style enums seem more doable. Is this something worth while tackling? If so, I would volunteer to help out (although I might need a bit of coaching).

@RReverser
Copy link
Member

RReverser commented Feb 18, 2018

Supporting the full power of Rust enums is probably a lot of work

Shouldn't be as TypeScript's support for sum types is pretty good (in some aspects better than Rust's one as it doesn't require explicit tags and so can cover any combinations of types).

For example,

enum MyEnum {
    A,
    B(u32),
    C(u32, u32),
    D { x: u32, y: u32 },
}

could be translated to

type MyEnum =
  | { tag: 'A' }
  | { tag: 'B', value: number }
  | { tag: 'C', value: [number, number] }
  | { tag: 'D', value: { x: number, y: number }
  ;

@alexcrichton
Copy link
Contributor

Sounds great to me to support! I'd also be fine starting with C enums and moving from there to variants with payloads.

I'd also be more than willing to help out with questions! I'm currently traveling now so I may be a bit slower but I'll help when I can.

The bad news is that this support won't be trivial based on the current architecture but I don't think it'd be too hard to extend?

@rylev rylev mentioned this issue Feb 22, 2018
@dbkaplun
Copy link

This is a great idea! IMO for signature parity, the translated enum should be:

type MyEnum =
  | { A: any }
  | { B: { '0': number } }
  | { C: { '0': number, '1': number } }
  | { D: { x: number, y: number } }
  ;

@RReverser
Copy link
Member

@dbkaplun That would make it much harder to match on it in JavaScript code as it doesn't support match statements like in Rust (yet). While with separate tag/value representation you'll be able to do:

function handleEnum(myEnum) {
  switch (myEnum.tag) {
    case 'A': ...; break;
    case 'B': ...; break;
    case 'C': ...; break;
    case 'D': ...; break;
  }
}

or

const myEnumHandlers = {
  A() { ... },
  B(value) { ... },
  C([x, y]) { ... },
  D({ x, y }) { ... }
};

function handleEnum(myEnum) {
  return myEnumHandlers[myEnum.tag](myEnum.value);
}

While it's possible to use something like Object.keys(myEnum)[0] to extract tag in your representation, it makes it more hacky and less readable IMO.

@dbkaplun
Copy link

dbkaplun commented Feb 22, 2018

I thought about your question earlier and it would be better to have a consistent interface than to pander to switch. What about:

if (myEnum.A) {
} else if (myEnum.B) {
}
...

@dbkaplun
Copy link

@alexcrichton can you weigh in?

@rylev
Copy link
Contributor Author

rylev commented Feb 22, 2018

At least in the Redux world this kind of enum (e.g., for things like Redux actions) are modeled in the tag/values approach. For example:

{type: "MyAction", foo: 1, bar: "baz"}

This works well and is easily modeled in plain JS or TypeScript. I think the other approach is fine, but I haven't seen it as much in the wild. Whatever the direction forward, I think it would be nice pick something familiar with JS devs.

@RReverser
Copy link
Member

RReverser commented Feb 22, 2018

I thought about your question earlier and it would be better to have a consistent interface

Consistent with what? There is no direct analogy to tagged enums in JS / TS either way. And { type, value } is actually at least consistent with how Rust enums are represented in memory.

Another reason for it would be that JS engines really don't like objects of different shapes (that is, different sets of properties) and would not be able to optimise any code using variables of such enum type.

Also, as pointed above, this is already quite common for JS APIs to use { type, ... } style for tagged enums in Redux, parsers (both ESTree AST for JavaScript and TypeScript's own AST use it) and many other domains where tagged unions are required, so it will be familiar to consumers and well optimised - win-win.

@alexcrichton
Copy link
Contributor

I haven't thought too too hard yet about enums with payloads yet unfortunately, but I think we'd probably want to basically "see what JS does" in terms of what most closely matches Rust and then do that.

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

Successfully merging a pull request may close this issue.

4 participants