# Variants

**URL:** <https://es.discourse.group/t/variants/574>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [December 8, 2020, 12:49pm UTC](https://es.discourse.group/t/variants/574 "2020-12-08T12:49:51Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![serras](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/serras/32/532_2.png) [@serras](https://es.discourse.group/u/serras)\
**Post date:** [December 8, 2020, 12:49pm UTC](https://es.discourse.group/t/variants/574/1 "2020-12-08T12:49:52Z")

</div>

Hi everybody. I have written a [proposal](https://gist.github.com/serras/45ce4737db96e800de298f61955828d5) 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.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [December 8, 2020, 7:45pm UTC](https://es.discourse.group/t/variants/574/2 "2020-12-08T19:45:55Z")

</div>

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

```javascript
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:

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

```

---

<div class="post-metadata">

**Author:** ![serras](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/serras/32/532_2.png) [@serras](https://es.discourse.group/u/serras)\
**Post date:** [December 9, 2020, 7:52pm UTC](https://es.discourse.group/t/variants/574/3 "2020-12-09T19:52:00Z")

</div>

Thanks for the catch @aclaymore. I've updated the [proposal](https://gist.github.com/serras/45ce4737db96e800de298f61955828d5) 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:

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

```

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [December 9, 2020, 10:34pm UTC](https://es.discourse.group/t/variants/574/4 "2020-12-09T22:34:05Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![jun-sheaf](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jun-sheaf/32/505_2.png) [@jun-sheaf](https://es.discourse.group/u/jun-sheaf)\
**Post date:** [December 14, 2020, 3:19pm UTC](https://es.discourse.group/t/variants/574/5 "2020-12-14T15:19:26Z")

</div>

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)

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [January 3, 2021, 5:03pm UTC](https://es.discourse.group/t/variants/574/6 "2021-01-03T17:03:45Z")

</div>

> 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.

---

<div class="post-metadata">

**Author:** ![JackWorks](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jackworks/32/260_2.png) [@JackWorks](https://es.discourse.group/u/JackWorks)\
**Post date:** [January 11, 2021, 7:39am UTC](https://es.discourse.group/t/variants/574/7 "2021-01-11T07:39:07Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![MaxGraey](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/maxgraey/32/651_2.png) [@MaxGraey](https://es.discourse.group/u/MaxGraey)\
**Post date:** [February 25, 2021, 12:37pm UTC](https://es.discourse.group/t/variants/574/8 "2021-02-25T12:37:51Z")

</div>

Variants aka Tagged Unions required some runtime works like extract and check discriminator also it required some storage which should passed by value not by reference. That's why in many languages ADT like Tagged Unions is builtin entity. TypeScript may only provide exhaustive matchings/verification for such variants.

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [March 2, 2021, 9:22am UTC](https://es.discourse.group/t/variants/574/9 "2021-03-02T09:22:02Z")

</div>

Engines already do this to tell apart primitives and even internal subtypes of primitives, even though most of them employ various tricks that make it extremely implicit and/or _extremely_ easy to check (because it's _so_ performance-sensitive). Here's a couple examples:

- You know V8's famous `Smi`s (and why people optimize so hard for 31-bit integers in perf-sensitive code)? That's technically equivalent to a two-variant union of `Smi(int31), HeapValue(pointer)`.
- V8 stores their `typeof` info as part of the type info, and when you add everything up with all their other type-based optimizations, after defining `class C { v = 1.5 }` and invoking it a few times, `new C()` takes approximately the same amount of memory as a boxed `1.5`.
- [SpiderMonkey on 64-bit platforms NaN-boxes all their pointers and uses the excess bits to include `typeof` info as well as needed info for 32-bit integers.](https://searchfox.org/mozilla-central/source/js/public/Value.h) So they take a more pure approach to tagging, and they leave doubles unboxed. Their object format is similar to V8's, though I'm not 100% sure what all optimizations they put into it (I'm more familiar with V8's).

For tagged unions, it's almost certainly going to be no more expensive than instantiating a class and setting a property, and have at worst identical memory usage. And yes, they will implement it as a reference, even if it's a value type per spec - they already have the framework in place for strings, symbols, the previously-proposed-and-since-abandoned SIMD.js vectors, and (now) bigints.
