GitHub

Exception Groups

An exception group is a named set of exception types. A catch arm naming a group expands to match every member.

Declaration

exception NotFound(path: string)

exception PermDenied(path: string, code: i64)



exception group IOError = NotFound | PermDenied

Declare a group with exception group Name = Member1 | Member2 | .... Each member has to be an exception that was declared earlier. The export modifier opens the group to importers.

Structured Exceptions

Each exception carries typed fields rather than a pre-formatted string. That shape lets you handle errors either case by case or in bulk:

exception UnexpectedToken(expected: string, got: string, span: Span)

exception MissingMain



exception group ParseError = UnexpectedToken | MissingMain

Zero-field exceptions (like MissingMain) omit parentheses in both declaration and pattern matching.

Catching Groups

Catching a group name expands to match every member:

try

  parse(tokens: tokens)

catch

  | ParseError ->

    // catches both UnexpectedToken and MissingMain

    Console.eprintln(val: "parse failed")

end

The form is sugar; the compiler expands | ParseError -> into one arm per member.

In the formal type system (type-system-formal.md), throws-rows are variant-precise. Each member of a group becomes its own row entry. A group catch subtracts the union of its member constructors from the row. The formal rules never see the group at all — only the expanded constructor list shows up (see caughtVariants and members in T-TryCatch).

Catching Specific Exceptions

For tighter handling, match each exception type and destructure its fields:

try

  compile(src: src)

catch

  | UnexpectedToken(expected: e, got: g, span: sp) ->

    Console.eprintln(val: "expected " ++ e ++ ", got " ++ g)

  | TypeMismatch(func_name: f, detail: d, span: sp) ->

    Console.eprintln(val: "in " ++ f ++ ": " ++ d)

  | MissingMain ->

    Console.eprintln(val: "no main function")

end

You can mix specific arms with group arms. The compiler checks each arm on its own.

Multi-Arm Catch

The catch clause supports two forms:

// Legacy: single variable binding

try body catch e -> handle(e: e) end



// Multi-arm: pattern matching on exception constructors

try

  body

catch

  | NotFound(msg: m) -> handle_missing(m: m)

  | PermDenied(msg: m) -> handle_perm(m: m)

  | IOError -> handle_generic()

end

Multi-arm catch desugars to catch __exn -> match __exn do ... end during compilation.

Group Composition

A group lists individual exceptions only; one group cannot reference another. For a “super-group” spanning several phases, name every member directly:

exception group CompileError =

  UnexpectedToken | MissingMain

  | TypeMismatch

  | SymbolNotFound

A group is expanded at the catch site, so there’s no nesting at runtime.


See also: Checked Exceptions and Capabilities, Type System — Formal Rules, Syntax