Questions on variants

We’ve got lots of variants in the game we’re making. What I want to know is if we’ve got data stored in the canister and we want to change the variant, what happens? The game design is currently in flux and we’re OK to wipe data… but in the future that may not be the case.

  1. With this variant, how would we go about adding a sixth element ie. #leeloo. Would it just magically work like an enum changing from 1-5 from 1-6 or would we have to perform an upgrade on the existing data.
  public type ElementVariant = {
    #air; #earth; #fire; #spirit; #water;
  1. What if we wanted to delete one?

  2. If you have { #a; #b } and you change it to { #a; #c; #b } does it act like an array where the only way to reference a value is through position? Is { #a; #b; #c } interchangeable with { #c; #b; #a };

  3. Type Equality. Is the following structure a good way to do a “generic attribute”? I was worried because of structural typing are the variants considered the same because they have the same type?

  public type UnitValue = {
    #acidity : Nat8;
    #hardness : Nat8;
    #opacity : Nat8;
    #resonance : Nat8;

The link for variants is confusing on the motoko guide as it goes to the Type Syntax page which is like an alien language.



It depends on how the variant is used.

In general, upgrades allow changing a stable variable to a supertype. A variant with more cases is a supertype, so if you just have a plain stable var x : ElementVariant, it is allowed to add a case (but not remove one).

But if, for example, your variable has type [var ElementVariant] then the array type itself cannot be changed, since mutable arrays are invariant. (We could perhaps relax this case, but it would be a bit fishy.)

In general, all schema changes that do not adhere to subtyping mean that there are cases for which the compiler wouldn’t know what to do with them (e.g., a variant case you removed). So you have to migrate the data by copying it over into a new variable and implement the desired mapping explicitly (either in the postupgrade hook or in some other place). Once migrated, you can remove the old type and variable.

If you don’t add or remove cases (or change the type of one), no action is required. Variant types, like objects, aren’t ordered, so {#a; #b} and {#b; #a} are the same type.

I’m afraid I did not understand your last question.


PS: In the near future, make sure to check the upgrade compatibility of your stable vars with the new compiler feature that we just released with Motoko 0.6.14 (see changelog and manual). This should be built into dfx shortly as well, once the relevant IC proposal made it through.


Oh nice, thanks. So is the text “tag” of a variant held in memory?

I guess I was confused as you can return #ok from a function without referencing the variant type definition.

I was thinking it was more like an “enum”, to save space or something.

No, it’s still a number, the compiler simply sorts the tags for you but the compiler hashes the tag names.

Hah yes, that’s also a good solution! Ok yeah the last question ignore, that was just me not understanding how variants worked.

Why is an IC proposal necessary to update a client tool like dfx?

The IC proposal is to add a metadata protocol that dfx can use to extract the (old) Candid and stable variable signature from a life canister that it wants to upgrade.