Hi everybody. I have written a proposal to bring variants to the language! Found in many other languages, variants indicate that a value is part of a set of choices, with some additional data attached depending on the choice. Redux actions are perfect examples of variants.


Hi @serras! When spreading a record with a tag would the tag be retained?

function setCountry(record, country) {
  return #{ ...record, country };

let p = #person { name: 'Alice', country: 'france' };
p = setCountry(p, 'germany');
Variant.tag(p); // 'person'?

or would code need to do this:

function setCountry(record, country) {
  return #[Variant.tag(record)]{ ...record, country };

Thanks for the catch @aclaymore. I've updated the proposal with how spreading would work for fields and tags. In short, you can now use #[v] to reuse the tag of another variant. Your setCountry function would be written as follows:

function setCountry(record, country) {
  return #[record] { ...record, country };

I am not convinced this warrants new syntax over the existing type: "tag" pattern. Neither of the points in your FAQ seem all compelling to me. And the downside (beyond just the burden of new syntax) is pretty significant: you lose the ability to compose this with the rest of the language naturally. There's a good example with spread properties above, but consider also destructuring (function foo({ type, value }) {....} is a nice pattern) and utilities like Object.fromEntries.

You can add special cases for some of these, but that mostly just increases the amount of stuff in the proposal without a corresponding increase in utility. By contrast, if you stick with the type field, you get composability with the rest of the language for free.

Records essentially exist in TypeScript through interfaces. This is just another proposal asking for TypeScript features in JS... just use TypeScript. This kind of runtime behavior just slows things down in Js (unless you want it to be sugar over something, but that’s pointless)

1 Like

This kind of runtime behavior just slows things down in Js

Not significantly - engines have long been able to optimize away conditions when it can infer a value is always of a specific type, and for the few it can't, there's only two types it can't reduce to at worst a pointer-width load + pointer-width test and branch (a single micro-op in x86 and 2-3 ARM micro-ops) for pure equality checking:

  • String equality comparison (if the slow path fails)
  • Floating point comparison (simple for SpiderMonkey 64-bit, complicated for most the rest as they store floats on the heap as they need to load both and then compare)

In polymorphic code, it's still fairly quick as long as you aren't comparing two strings, and even then, in all of this, we're talking nanoseconds and single microseconds - this rarely means much unless you're writing a DOM framework, 3D renderer, or whatever.

The prior art of this proposal seems duplicated with the Enum proposal. And if the Enum proposal becomes ADT Enum, it will be better type it with TypeScript.