union (a sum type)
A union is a base declared with abstract record, together with the record types that inherit from it.
Forms that compile (8)
Section titled “Forms that compile (8)”| Syntax | Description | Note |
|---|---|---|
abstract record Shape; record Circle(float R) : Shape; | Declaration and value generation | |
s switch { Circle c => ..., _ => ... } | Branching by type | |
s switch { Circle c => ..., Rect r => ... } | Branching without a default case | |
A a => ..., B b => ..., C c => ... | Branching over 3 or more types | |
s is Circle | Checking the type | |
void Use(Shape s) | Receiving as a parameter | |
Shape Make() | Returning as a return value | |
Shape[] all = new Shape[2]; | Array element |
Forms that don’t compile (4)
Section titled “Forms that don’t compile (4)”| Syntax | Description | Error | Reason | Alternative |
|---|---|---|---|---|
private Shape held; | Holding as a field | TUKI0001 | not yet | Put the kind enum and the payload in separate fields |
abstract record Node; record Pair(Node L, Node R) : Node; | A definition that includes itself (recursion) | TUKI0001 | runtime | Represent a tree with an array and indices |
Debug.Log(s) | Passing a union value out as is | TUKI0001 | runtime | Branch by type first, then pass the extracted value |
s switch { Line l when … => … } | Attach a condition to a union type pattern | TUKI0001 | not yet | Move the condition into the case body, or split the case without when |
Where it can live
Section titled “Where it can live”A union value can go in local variables, arguments, return values, and arrays. It can’t be held in a field. To keep it as state, put the enum value that represents the kind and each type’s contents in separate fields.
Relation to interfaces
Section titled “Relation to interfaces”An interface attached to a value type (struct / record) is held in the same form: both become a single number that represents which type is currently held, plus one heap variable per type, laid out in sequence. Only an interface attached to a Behaviour uses a different mechanism — it points to the Behaviour itself.
How many heap variables it uses
Section titled “How many heap variables it uses”| How it is counted | The sum of the payloads of every type, plus one for the number identifying the kind |
| Overlaying | No. It isn’t sized to the largest type |
| Adding one more type | It grows by that type’s payload |
| Limit | Udon’s execution model |
Declaration and branching
Section titled “Declaration and branching”Declaration and value generation
Section titled “Declaration and value generation”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnDeclare : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }}Branching by type
Section titled “Branching by type”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnSwitchWithDefault : TsukimiBehaviour{ void Start() { Shape s = new Rect(2f, 3f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 6 }}Branching without a default case
Section titled “Branching without a default case”When the cases are exhaustive, no default case is needed.
using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnSwitchExhaustive : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, }; Debug.Log(a); // => 1 }}Branching over 3 or more types
Section titled “Branching over 3 or more types”using UnityEngine;using Tsukimi;
public abstract record Shape;public record A(int N) : Shape;public record B(int N) : Shape;public record C(int N) : Shape;
public class UnThreeCases : TsukimiBehaviour{ void Start() { Shape s = new A(1); int v = s switch { A a => a.N, B b => b.N, C c => c.N, _ => 0 }; Debug.Log(v); // => 1 }}Checking the type
Section titled “Checking the type”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnIsPattern : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); bool isCircle = s is Circle; Debug.Log(isCircle); // => true }}Storage
Section titled “Storage”Receiving as a parameter
Section titled “Receiving as a parameter”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnParameter : TsukimiBehaviour{ void Start() { Use(new Circle(1f)); }
private void Use(Shape s) { float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }}Returning as a return value
Section titled “Returning as a return value”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnReturnValue : TsukimiBehaviour{ void Start() { Shape s = Make(); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }
private Shape Make() { return new Circle(1f); }}Array element
Section titled “Array element”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnArray : TsukimiBehaviour{ void Start() { Shape[] all = new Shape[2]; all[0] = new Circle(1f); all[1] = new Rect(1f, 2f); float total = 0f; for (int i = 0; i < all.Length; i++) { Shape s = all[i]; float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; total += a; } Debug.Log(total); // => 3 }}