Selection types describe a closed set of selectable values.
Typical has four selection type forms:
one ofone value ofone case ofmany of
They share a common declaration shape and editor model, but each form answers a different question.
Day is one of (
monday
tuesday
wednesday
)
BitWidth is one value of (
16
32
64
)
Message is one case of (
default()
text(value is string)
image(url is Url)
)
Permission is many of (
read
write
execute
)
one of selects exactly one named identity.
one value of selects exactly one primitive value.
one case of selects exactly one case, where each case may carry its own payload shape.
many of selects zero or more named identities as flags.
Structural Typing
Selection types are structurally typed.
The declaration name is a label for a closed set of selectable values. It is useful for annotation, access, documentation, and editor completion, but it is not intended to create nominal type weight.
Two selection types with the same compatible selection set are type-compatible even if they were declared under different names.
SmallWidth is one value of (
16
32
)
NetworkWidth is one value of (
16
32
)
SmallWidth and NetworkWidth describe the same selection set.
This differs from ordinary Typical classes, which are nominal by default.
For identity-based selection types, names are part of structural compatibility.
For one of, the structure is similar to a constant object shape whose keys are entry names and whose values are entry values.
A is one of (
name = "bob"
number = 12
)
B is one of (
name = "bob"
number = 12
flag = true
)
A has the structure { name is "bob", number is 12 }.
B has the structure { name is "bob", number is 12, flag is true }.
B is compatible with A. A is not compatible with B.
Compile-Time Shape
Scalar selection declarations behave like TypeScript const enum declarations in spirit.
They provide names, grouping, checking, completion, and a value access surface during compilation. They are expected to disappear as standalone runtime entities when lowered, leaving the selected underlying values or flag representations.
one case of selections lower to tagged values because each case may carry payload data.
The compiler may still generate backend helper code when a target needs it for ergonomics or soundness.
Type And Access Surface
Every selection declaration creates a type name and an access surface.
The type name is used in annotations.
foo(width is BitWidth)
(
)
The access surface is used to name selectable entries.
foo(BitWidth.32)
For one of and many of, entries are named identities.
Permission.read
Permission.write
For one value of, entries are the values themselves.
BitWidth.32
Name."bob"
For one case of, entries are case constructors.
Message.default()
Message.text("hello")
This preserves the labeled completion behavior that literal unions normally lose.
Raw Values
Because scalar selection types are structural, a raw value that belongs to the selection set is compatible with that selection type.
foo(width is BitWidth)
(
)
start
(
foo(16)
foo(BitWidth.16)
)
If a raw value does not belong to the selection set, the compiler generates a notice.
one case of values are created through case constructors rather than raw scalar values.
Numeric compatibility follows the rules described in 04-Provisional-Numbers.
Inferred And Explicit Values
one of and many of can infer underlying values.
one of infers ordinary increasing integer values starting at 1.
Day is one of (
monday
tuesday
wednesday
)
many of infers increasing powers of two starting at 1.
Permission is many of (
read
write
execute
)
Authors may also specify values explicitly. When some entries have explicit values and others do not, the compiler infers the missing values from the surrounding declared form.
Day is one of (
monday = 1
tuesday
wednesday = 10
thursday
)
In this example, tuesday infers to 2 and thursday infers to 11.
Permission is many of (
read = 1
write
execute = 16
hidden
)
In this example, write infers to 2 and hidden infers to 32.
one value of does not infer entry values. Its entries are already the selected values. Numeric literal values default to platform int unless another type is forced by context.
one case of does not infer entry values. Its cases are constructor shapes rather than named constants with scalar values.
Constants And Spreads
Selection entries may reference scope-reachable values when those values are constant and statically resolvable.
letterA = "a"
letterB = "b"
Letters is one of (
a = letterA
b = letterB
)
Selection types support spread.
PrimaryColor is one of (
red = 1
green = 2
blue = 3
)
Color is one of (
...PrimaryColor
alpha = 4
)
one value of may spread a fixed constant array.
span = [2, 3, 4]
Nums is one value of (
1
...span
5
)
Identity-based selection types may spread a fixed constant object. The object keys become entry names and the object values become entry values.
object = {
key1 = 1
key2 = "key2"
}
Example is one of (
key0 = 0
...object
key3 = 3
)
flags = {
red = 1
green = 2
blue = 4
}
ColorFlag is many of (
key0 = 0
...flags
key3 = 8
)
one case of may spread another one case of declaration.
CommonMessage is one case of (
default()
text(value is string)
)
Message is one case of (
...CommonMessage
image(url is Url)
)
Spread preserves source order for inference, iteration, and editor display.
Order
Selection entries are order-preserving.
Order affects inferred values. Reordering entries with inferred values can change the program's underlying values.
Order also determines the default iteration, documentation, and editor display order.
Duplicates
Duplicate rules are feature-specific.
one of permits duplicate underlying values. Duplicate values create aliases for the same selected value.
one value of does not permit duplicate values.
one case of does not permit duplicate case names.
many of permits duplicate underlying values for consistency with one of. Composite overlapping flag values are also permitted when written explicitly.
None
none is not a shared selection type concept.
It is special only for many of, where the zero value represents no selected flags.
For one of, one value of, and one case of, an author who wants a none-like entry must define it explicitly as an ordinary member of the selection set.
Members And Extensions
Selection types may define functions.
Selection types may also be extended with extension classes.
Selection types do not support properties. Properties would be visually and semantically confusing because selection entries are also accessed through the declaration's access surface.
Selection types do not support fields, constructors, static scopes, or ghosts.