Skip to main content
Use ensure with is to validate at runtime and narrow types. Forst has no exceptions. Coercion is never silent.

Basic ensure

Reject invalid input at runtime and attach a domain error with or:
  • ensure x is Condition: runtime check; failure returns or propagates an error depending on context
  • or …: attach a domain-specific failure when the condition does not hold
The compiler understands the is operator for built-in constraints (LessThan, Min, Present, Ok, …).

Nil and presence

Forst uses Nil for absence checks today. Full optional value types (T | Nil) are still planned; what works now is nil on nilable types (pointers, Map, Array, Error), ensure … is Nil(), and *T .Present() / .Nil() on pointer fields.
Negated forms work for error checks:

Result and Ok narrowing

Map lookups and other partial operations yield Result(V, Error) instead of comma-ok tuples:
Comma-ok assignment (v, ok := m[k]) is not Forst syntax. Handle failure explicitly with ensure avail is Ok() before using the success value. At call sites, narrow success payloads:

If-branch narrowing

if x is … can refine a variable’s type inside the branch. That helps when you need conditional logic instead of early return:

Compared to Go and TypeScript

Forst requires ensure with English keywords so validation stays visible in the source.

Caveats

ensure and narrowing are experimental. Behavior and diagnostics are still maturing. See the roadmap.

Checks run once

A successful ensure or if check narrows the type for following lines. The compiler does not watch the value for later changes. Reassignment or field mutation can leave runtime state out of sync with the narrowed type. See Type guards § Checks run once for a concrete example with guards.
Narrowing reflects a single check at compile time. It does not lock the value at runtime.

Simple names

Successor narrowing after ensure works best on simple identifiers (ensure x is …, then use x). Compound paths like ensure req.state is … have partial support. Hover may show the predicate, but the typechecker does not fully re-register a refined type on the path.

After if chains

Narrowing inside an if branch does not always carry past the end of the chain. The continuation may see the pre-if type until union types exist. Branch merge after ensure successors is also still expanding.

No comma-ok

v, ok := m[k] is not Forst syntax. Map lookups and similar partial operations return Result(V, Error). Use ensure avail is Ok() before using the success value.

Optionals still partial

Full optional value types (T | Nil) are planned. Today use nil on nilable types, ensure … is Nil(), and *T .Present() / .Nil() on pointer fields.

What gets emitted

Ensure statements lower to Go error returns and conditionals. Failures are structured values returned through Go’s error path. Custom failures use nominal error types when you provide them with or.

Try it

From the Forst repository:

Errors and Result

Nominal error types and structured failures.

Type guards

User-defined predicates for domain rules.