Specifications / Language

Introduction

Osprey is a statically typed functional language built around algebraic effects. Application code requests typed operations; handlers supply their implementation. Osprey compiles through LLVM to native code, WebAssembly, and C ABI libraries for iOS, macOS and Android. Default (.osp), ML (.ospml) and Python flavor (.py with the Osprey marker) lower to osprey_ast::Program before type checking and code generation. Their shared language and flavor-specific boundaries are defined in Language Flavors. Python flavor implements a documented subset of Python syntax; CPython libraries use the separate interop boundary.

Language shape

  • Callable algebraic-effect handlers: handler E { ... } creates a policy, and h(work) runs a callback under it. handle E { ... } handles the rest of its block.
  • Hindley-Milner inference with optional, constraining type annotations.
  • Immutable bindings and explicit mutable bindings.
  • Expression-oriented branching through match, ternaries, and the Default flavor's if/else expression.
  • Checked operation requirements and explicit value/control operation modes. The effects contract defines scoped rows and continuation semantics; plan 0016 distinguishes current support from unfinished requirements.
  • Result<T, E> values for structured failures.

Failure safety — [FAILURE-EXPLICIT]

Every operation that can fail MUST expose that possibility in its static type, either as Result<T, E> or as an algebraic effect that is discharged by a statically known handler. A language operation MUST NOT panic, silently wrap, substitute a zero value, or erase an error in order to produce a plain T.

Osprey has no panic, abort or exit operation, and none may be added: no builtin, prelude function, runtime entry point or extern binding may end the program in place of returning an error. Source that binds a native function which panics, aborts or exits violates this rule wherever it appears, including published packages.

[ENTRY-EXIT-STATUS] A program ends by returning. When a user-written main yields an int, that value is the process exit status, truncated by the operating system to its status width; any other main and a program without a main exit 0. A failed test outranks main's status ([TESTING-EXIT]).

There is no implicit conversion from Result<T, E> to T. In particular, bindings and assignments, function arguments (including concurrency operations), comparisons, interpolation, function-value calls, and declared scalar returns preserve the Result wrapper or are rejected. A caller obtains a T only by exhaustively matching the Result or by supplying an explicit fallback with ?:.

Arithmetic uses plain numeric values. The required totality contract forbids implicit traps and silent wraparound; the host runtime discharges faults through an installed Arith policy. Current device kernels instead abort on recorded arithmetic faults, even with a recovering host policy. Device recovery records that delivery gap and the explicit total helpers available to kernels.

Raw foreign declarations may expose a C integer status as ABI data. Safe Osprey-facing APIs MUST translate a failing status into Result or a typed effect before returning it to ordinary language code.

Runtime and platforms

  • Fibers and typed channel communication.
  • Native builds use tracing GC by default; Perceus ARC is the alternative managed backend. Unmanaged allocation requires explicit --memory=none and does not reclaim every allocation. Static-memory checking is not implemented.
  • Native C interoperability and built-in HTTP and WebSocket runtimes.

Each later chapter states narrower availability or platform limits beside the feature it specifies.