Skip to content

Add support for ADT enums #11

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
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Add support for ADT enums #11

wants to merge 2 commits into from

Conversation

rbuckton
Copy link
Collaborator

This adds a rough description of ADT enum support.

@rbuckton
Copy link
Collaborator Author

cc: @rwaldron, @Jack-Works

"string name",
[expr]
// A special `Enum.ADT` mapper exists that can be used to define algebraic data types:
enum Message of Enum.ADT {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how this dynamic dispatch approach (delegate to Enum.ADT[@@toEnum]) works with TypeScript.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may not matter too much for native enum due to differences in how we handle enum vs. const enum. For something like enum Color of Number { red, green, blue }, default members would be TypeFlags.EnumLiteral | TypeFlags.Number.

I'm currently iterating on the ADT enum approach. I'm considering the possibility of having the of mapper only apply to Scalar members that don't use ADT enum syntax, and having ADT enum members (that use {} or ()) always produce an ADT enum member factory. Essentially, that would mean that of would only apply to uninitialized scalar values.

let x = Color.red;
let y = Named["string name"];
// ADT enum construction
const m1 = Message.Move{x: 10, y: 20};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little skeptical about {} syntax but () syntax is good to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really like to be able to leverage this for the following scenario:

enum Node of Enum.ADT {
  Identifier{ escapedText, pos, end },
  StringLiteral{ text, pos, end },
  NumericLiteral{ text, pos, end },
  BigIntLiteral{ text, pos, end },
  ...
  BinaryExpression{ left, operator, right, pos, end },
  PropertyAccessExpression{ expression, name, pos, end },
  ElementAccessExpression{ expression, argumentExpression, pos, end },
  ...
}

Comment on lines +134 to +137
| @@toEnum | `"Symbol.toEnum"` | A method that is used to derive the value for an enum member during _EnumMember_ evaluation. |
| @@formatEnum | `"Symbol.formatEnum"` | A method of an _enum object_ that is used to convert a value into a string representation based on the member names of the enum. Called by `Enum.format`. |
| @@parseEnum | `"Symbol.parseEnum"` | A method of an _enum object_ that is used to convert a member name String into the value represented by that member of the enum. Called by `Enum.parse`. |
| @@propertyConstruct | `"Symbol.propertyConstruct"` | A method of an ADT _enum member_ that is used to construct an ADT record-like enum value using object literal-like syntax. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to reduce the number of new well-known symbols. Is it possible to merge them into one?

@@enum: { format?, parse?, construct?, from? }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe parse and format, but not the others:

  • @@toEnum goes on the mapper passed to of such as Number, String, etc.
  • @@formatEnum and @@parseEnum go on the constructor produced by the enum declaration_.
  • @@propertyConstruct goes on an ADT enum tagged record member.

Those are three wholly different placements. @@formatEnum and @@parseEnum provide a way to access the underlying enum data used to relate a key to a value and vice versa, and are symbols rather than internal slots so that existing enum-like constructs can be adapted to work with Enum and can be produced by a transpiler.

README.md Outdated
1. Set `value` to be the result of evaluating _Initializer_.
1. If the _enum member_ has an _AgebraicDataTypeParameterList_ (i.e., `(a, b, c)`), then:
1. Let `options` be a new Object.
1. Set `options.kind` to `"tuple"`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not pass them as array/object directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the declaration, not instance construction. There's no "object" to pass, only the property names or parameter list.

is transposed the following representation:

```js
const msg = Message.Move[Symbol.propertyConstruct](#{ x, y });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need R&T here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, yes. I'm of the opinion that ADT enum should have the same requirements as records and tuples as to the kinds of values they can hold. One reason is data portability between realms, along with the potential for a future shared enum that could tie into shared struct and multi-threaded JS. I'd also like to have Option.Some(1) === Option.Some(1) hold.

@ljharb
Copy link
Member

ljharb commented Oct 18, 2022

What I don't see here is a clear explainer for what an ADT enum is (beyond just the initialism expansion), and why it's important to have that feature in the language.

@rbuckton
Copy link
Collaborator Author

What I don't see here is a clear explainer for what an ADT enum is (beyond just the initialism expansion), and why it's important to have that feature in the language.

That is forthcoming, this is still fairly early.


// `of` clause:

// Each auto-initialized member value is a `Number`, auto-increments values by 1 starting at 0.
enum Colors of Number {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If only derived from Number, how might these strictly compare to other number types, or would they only explicitly compare to themselves? I suppose the latter makes more sense anyway.

Automatic initialization is supported by way of an _Enum Mapper Object_ provided via an `of` clause:

```js
// Number-based enum with auto-increment
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is mixing of auto-incremented values and explicit allowed as in some languages e.g., C#? In that case, should this proposal be explicit in how that will work e.g., auto-increments from the previous auto or explicit value?

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 this pull request may close these issues.

4 participants