Skip to content

union (a sum type)

A union is a base declared with abstract record, together with the record types that inherit from it.

SyntaxDescriptionNote
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 CircleChecking the type
void Use(Shape s)Receiving as a parameter
Shape Make()Returning as a return value
Shape[] all = new Shape[2];Array element
SyntaxDescriptionErrorReasonAlternative
private Shape held;Holding as a fieldTUKI0001not yetPut 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)TUKI0001runtimeRepresent a tree with an array and indices
Debug.Log(s)Passing a union value out as isTUKI0001runtimeBranch by type first, then pass the extracted value
s switch { Line l when … => … }Attach a condition to a union type patternTUKI0001not yetMove the condition into the case body, or split the case without when

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.

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 it is countedThe sum of the payloads of every type, plus one for the number identifying the kind
OverlayingNo. It isn’t sized to the largest type
Adding one more typeIt grows by that type’s payload
LimitUdon’s execution model
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
}
}
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
}
}

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
}
}
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
}
}
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
}
}
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
}
}
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);
}
}
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
}
}