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

Supports for ADT enum? #6

Open
petamoriken opened this issue Aug 16, 2018 · 12 comments
Open

Supports for ADT enum? #6

petamoriken opened this issue Aug 16, 2018 · 12 comments

Comments

@petamoriken
Copy link

The enums in Swift, Rust and Scala (case class) can have values and it can be unwrapped by pattern matching.

// swift
enum Command {
    jump
    run(velocity: Int)
    punch(target: Enemy, power: Int)
}

let command = Command.run(20)
switch command {
case .jump:
  print("jump")
case .run(let velocity):
  print("run(velocity: \(velocity))")
case .punch(let target, let power):
  print("run(target: \(target), power: \(power))")
}

It would be more useful to support Algebraic Data Types (ADT), so I would like to include it in this proposal.

enum Command {
  jump
  run(velocity)
  punch(target, power)
}

// Stage 1 Pattern Matching
const command = Command.run(20);
case (command) {
  when Command.jump -> {
      console.log(`jump`);
  }
  when Command.run(velocity) -> {
      console.log(`run(velocity: ${velocity})`);
  }
  when Command.punch(target, power) -> {
      console.log(`punch(target: ${target}, power: ${power})`);
  }
}

// types
typeof Command.run === "function";
typeof Command.run(20) === "object";

// not necessary if implementation becomes difficult
Command.run(20) === Command.run(20);
@Tarnadas
Copy link

FYI: rustwasm/wasm-bindgen#31

wasm-bindgen does not yet support enums with payload, because JS itself doesn't do so.

@mheiber
Copy link

mheiber commented Nov 28, 2019

@petamoriken I noticed that the Prior Art section of proposal-enum doesn't mention enums like the ones you mention in Rust, Swift, and Scala. (or the ML-family language features that inspired the Rust/Swift/Scala enums)

Even if the proposal doesn't grow to allow enums with payloads, it would be helpful for the proposal to mention them and say why they aren't included.

@treybrisbane
Copy link

@rbuckton Any thoughts on this?

Given that newer languages such as Rust, Swift, etc are all generalising the concept of enums into rich ADTs, it seems strange to base a modern JS proposal on "traditional" enums...

I can think of numerous benefits of an ADT-oriented approach over a "traditional" one, particularly in the functional programming space. Some off the top of my head:

  • Easy functional utility types
    enum Result {
        Failure(value),
        Success(value),
    }
    
    enum Optional {
        None,
        Some(value),
    }
  • ADT-oriented domain modelling:
    enum ContentType {
        HTML(charSet),
        JSON(charSet),
        FormData(boundary),
    }
    
    enum ContentDisposition {
        Inline,
        Attachment(fileName, fileNameStar),
        FormData(fieldName, fileName, fileNameStar),
    }
  • Opportunities for first-class integration with pattern matching (including potential exhaustiveness checks using something like TypeScript)

I'm not sure I'd personally support the introduction of "traditional" enums into JS, as I feel they're too primitive and don't add enough value to justify the extra syntax. In contrast, I'd be hugely in favour of introducing more modern, ADT-oriented enums to the language, as they're a more flexible construct that provides vastly superior modelling capabilities. 🙂

@mheiber
Copy link

mheiber commented Feb 22, 2020

I'm curious about what the benefits of JS enum-with-payload are as compared to the following pattern I see a lot in TypeScript:

type Result<Success, Failure> = {
     kind: "success",
     value: Success
} | {
    kind: "failure",
    value: Failure,
};

function handleResponseOrThrow<S, F>(result: Result<S, F>): S {
    switch(result.kind) {
         case "success":
               // value is of type Success here
              return result.value;
         case "failure":
              // value is of type Failure here
              throw Error(`failed ${result.value}`);
         default:
              // next line will error at compile time if there is an unhandled enum variant
              // (something other than "success" or "failure")
              const _exhaustivenessCheck: never = result;
              // really should be unreachable
              throw Error("unreachable");
     }
}

A JS solution could probably have more succinct pattern matching and exhaustiveness checking.

@Jack-Works
Copy link

I do hope I have ADT enum at least once a week but I think TS won't do that cause JS doesn't have that. I'm strong desire to see ADT enum in JS.

@rbuckton
Copy link
Owner

rbuckton commented Apr 3, 2020

One possibility of allowing enums with payloads would be to leverage some of the design around struct in https://github.com/rbuckton/proposal-struct and allow only wasm-primitive and struct-compatible types in the payload definitions:

enum Result {
  Failure(i32, box), // `box` currently indicates any ECMAScript language value
  Success(box)
}
const res = Result.Failure(404, "not found");

However, this needs a form of pattern matching to be truly usable.

@Jack-Works
Copy link

Without pattern matching, it is still useful. It can simplify the creation of the tagged union.

For example

const f = () => Result.Success(123)

const x = f()
switch (x.kind) {
    case Result.Success:
        alert(x.items[0]) // 123
}

@treybrisbane
Copy link

@rbuckton I was thinking we could leverage something similar to Scala's Extractor pattern (i.e. give enum values an unapply method that returns a tuple containing the original inputs). That would enable extraction of values in the absence of pattern matching, and could hypothetically be worked into any pattern matching implementation.

@mheiber
Copy link

mheiber commented Apr 16, 2020

One possibility of allowing enums with payloads would be to leverage some of the design around struct in https://github.com/rbuckton/proposal-struct and allow only wasm-primitive and struct-compatible types in the payload definitions:

enum Result {
  Failure(i32, box), // `box` currently indicates any ECMAScript language value
  Success(box)
}
const res = Result.Failure(404, "not found");

Would Failure(1, box) === Failure(1, box) evaluate to true?

@mheiber
Copy link

mheiber commented Nov 3, 2020

@rbuckton I was thinking we could leverage something similar to Scala's Extractor pattern (i.e. give enum values an unapply method that returns a tuple containing the original inputs). That would enable extraction of values in the absence of pattern matching, and could hypothetically be worked into any pattern matching implementation.

Ideally, there would be a custom way of extracting values, so that data structure authors can maintain their API boundaries.

unapply is one way, but view patterns are more general (could be used with things that are not enums). So unapply might not be needed if proposal-pattern-matching supports view patterns or there is a follow-on proposal for view patterns.

@rbuckton
Copy link
Owner

I wrote up a sketch for possible ADT support a few months ago, though the current struct proposal doesn't quite align with that design anymore: https://gist.github.com/rbuckton/4a5108fab40ac90551bf82d9884711b5

I also just wrote up this as another example of how this could work using normal classes/functions: https://gist.github.com/rbuckton/192c2922650e05a1ca9cd7c01be7fc6c

@rbuckton
Copy link
Owner

@rbuckton I was thinking we could leverage something similar to Scala's Extractor pattern (i.e. give enum values an unapply method that returns a tuple containing the original inputs). That would enable extraction of values in the absence of pattern matching, and could hypothetically be worked into any pattern matching implementation.

Good news, https://github.com/tc39/proposal-extractors reached Stage 1 at the last TC39 meeting, so hopefully we will be able to build that up and build on it for ADT enums.

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

6 participants