Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Function

switch expression

subject switch { pattern => value, …, _ => value }

Choose a VALUE by matching a subject against patterns. Arms are tried in order and the first match wins. Patterns are a constant, a relational comparison, a type test, or the `_` discard. Over a closed vocabulary — an enum or a bool — a missing arm is a compile error rather than a runtime throw.

stable2 examples compiled by CIfunctioncontrol-flowpatternauthoring

Summary#

subject switch { … } is an EXPRESSION: it produces a value. Arms are tried top to bottom and the first one that matches wins. It is the form to reach for wherever a value is wanted rather than a statement — notably inside a render block, which takes no statements.

Over a closed vocabulary (an enum, or a bool) a switch expression with no _ arm must cover every member, and a gap is a compile error.

Signature#

subject switch {
  Member        => value,     // a CONSTANT — an enum member or a literal
  >= 1000.0     => value,     // a RELATIONAL comparison: < <= > >=
  Circle c      => value,     // a TYPE pattern, with a required binding
  _             => value,     // the DISCARD — matches anything
}

Description#

Arms are tried in order, and order is the whole design of a relational ladder. 1500000 satisfies both >= 1000000 and >= 1000; writing the larger bound first is what makes it answer "M".

A relational arm compares against a constant and may use any of <, <=, >, >=. There is no == form because a bare constant arm already means equals.

Exhaustiveness is checked only where a vocabulary is closed. An enum and a bool have a knowable set of values, so every one must have an arm or the compile fails. A number does not, so a switch over one always needs _ — including a relational ladder, because proving that < 0 and >= 0 between them cover every number is arithmetic the compiler does not attempt. It asks for a _ you may not strictly need rather than claiming a gap is covered when it is not.

A missing enum arm is an ERROR, not a warning. This is a deliberate divergence from C#, which warns at compile time and throws at run time. A throw inside a render expression is a blank page instead of a message, so the check that prevents it has to be the one that cannot be ignored.

The subject is read once per arm test, so it must be re-readable at no cost — a name, a member access, an index. Anything that could do work (a call, an await) is refused, and the refusal names the one-line fix: assign it to a local first.

A when guard is not part of this form. Use an if for that.

Examples#

public enum MarkKind { Line, Area, Column }

// RELATIONAL — the compact-number ladder. The larger bound comes first because the first match wins.
string Compact(int n) {
  return n switch { >= 1000000 => "M", >= 1000 => "K", _ => "" };
}

// All four ordering operators.
string Band(int n) {
  return n switch { < 0 => "neg", <= 10 => "small", > 100 => "big", _ => "mid" };
}

// A CONSTANT arm over an enum. No `_`: every member has an arm, which is what makes adding a member to
// the vocabulary a build failure at each site that must decide about it.
string Family(MarkKind k) {
  return k switch { Line => "stroke", Area => "stroke", Column => "fill" };
}

// A bool is a closed vocabulary too, so both cases are exhaustive with no `_`.
string YesNo(int n) { return (n > 0) switch { true => "yes", false => "no" }; }
class Shape { public string Name; }
class Circle : Shape { public double Radius; }

// `Circle c` binds `c` at the narrower type, so `c.Radius` reads inside that arm.
string Describe(Shape s) {
  return s switch { Circle c => $"circle r={c.Radius}", _ => s.Name };
}

See also#

  • switch — the switch STATEMENT, which branches control rather than producing a value
  • if / else — the two-way conditional, and where a when-guard-shaped test belongs
  • Testing which class a value isis / as / a cast / OfType<T>(), the other places a type is asked about
  • Enums — why a closed vocabulary is what makes exhaustiveness checkable

Related

switch

Branch on a value against constant case labels. Only the matched section runs (no fall-through); a default section…

if / else

Conditional branching, exactly as in C#. The condition must be a bool — there is no truthiness, so a null or a number…

Testing which class a value is

`s is Circle` asks which type a value actually is at run time, answering by the value's own type rather than the type…

Enums

A fixed set of named values, used as a member type. By default an enum stores as a compact number; add [Type(string)]…