Osprey — safe, fast functional programming without the clutter.

A practical language with strong inferred types, explicit errors, first-class effects and lightweight concurrency. Osprey compiles through LLVM to native programs, with no VM or JIT warm-up.

Strong types and explicit failure, without noisy annotations.

type Lookup = Found { value: int } | Missing

fn doubleFound(result) = match result {
  Found { value } => Success(value * 2)
  Missing => Error("value not found")
}

match doubleFound(Found { value: 21 }) {
  Success(value) => print("result: ${value}")
  Error(message) => print("error: ${message}")
}

Built for real programs

Osprey combines functional clarity with systems-oriented deployment. The features fit together around four priorities: practical code, safety, performance and elegance.

Practical by design

Write direct code, call existing C libraries and compile native binaries or WebAssembly. Useful abstractions should remove work, not create ceremony.

Safe where it matters

Expected failures use results, pattern matching checks every case, arithmetic does not silently wrap and fibers avoid shared mutable state.

Native performance

LLVM compilation, lightweight fibers, persistent collections and selectable memory management give high-level code a systems-oriented runtime.

Functional elegance

Type inference, immutable data, direct-style concurrency and first-class effects keep the important parts visible and the plumbing out of the way.

Safety without busywork

Make invalid states and hidden failure harder to write

Osprey uses inferred static types, explicit result values and exhaustive pattern matching. You get strong checks without repeating obvious type information throughout the program.

  • Errors are values instead of hidden exceptions or nulls
  • Pattern matching checks that every data case is handled
  • Checked arithmetic reports overflow instead of silently wrapping
  • Osprey-managed memory is safe; calls into C cross that safety boundary
type Payment = Approved { receipt: string }
             | Declined { reason: string }

fn describe(payment) = match payment {
  Approved { receipt } => "paid: ${receipt}"
  Declined { reason } => "declined: ${reason}"
}

Effects without dependency plumbing

effect Logger {
  log: fn(string) -> Unit
}

fn process(orderId) !Logger = {
  perform Logger.log("processing ${orderId}")
  orderId * 2
}

handle Logger
  log message => print(message)
in process(21)

Ask for work where it is needed; decide how it happens at the edge

Code can log, store data or retry work without passing service objects through every intermediate function. Swap a handler in a test without changing the code under test.

Effect inputs and outputs are type-checked. Complete compile-time checking for missing effect setup is still in progress; a missing handler currently produces a runtime diagnostic.

Concurrency that stays direct

Lightweight fibers, message passing and no function colouring

Spawn concurrent work without turning every function in the call chain into a special async kind. Fibers communicate by moving or copying values through channels rather than sharing mutable state.

  • Lightweight tasks instead of one operating-system thread per job
  • Channels for isolated communication
  • Ordinary direct-style functions and results
fn work(n) = n * n

let first = spawn work(6)
let second = spawn work(7)

print("${await(first)}, ${await(second)}")

let messages = Channel(1)
send(messages, "done")
print(recv(messages))

Fast programs, deliberate trade-offs

Osprey compiles through LLVM with no VM or JIT warm-up. Choose the memory strategy that fits the build, and use the same language semantics across those choices.

Native and WebAssembly

Build native binaries for systems-oriented deployment or target WebAssembly for the browser.

Selectable memory

Native builds can use the default non-reclaiming allocator, tracing garbage collection or Perceus reference counting.

Persistent collections

Immutable lists and maps reuse unchanged structure, so an update does not copy the entire collection.

Direct C interop

Declare and link the C functions you need. C code remains outside Osprey's memory-safety guarantee.

Performance claims belong with measurements. See the reproducible cross-language benchmarks →

Two flavors, one language

Choose familiar braces and calls or clean ML-style layout and currying. Both flavors use the same type system, effects, runtime and backends; neither is a reduced or secondary form of Osprey.

Default .osp

Braces, fn and familiar function calls for developers coming from C#, Go, Rust, Java, Kotlin or Swift.

fn greet(name) = {
  let message = "Hello, ${name}"
  print(message)
}

greet("Ada")

ML .ospml

Layout-sensitive syntax, currying and whitespace application for developers at home in ML, OCaml, F# or Haskell.

greet name =
  message = "Hello, ${name}"
  print message

greet "Ada"

Files choose a flavor independently. Cross-flavor multi-file integration is the design direction and is not yet described as complete. Read about the two flavors →

Try Osprey

Use the browser playground, or install the compiler and build a native program.

Browser playground

Compile and run Osprey without installing anything.

Open Playground

macOS / Linux

brew install nimblesite/tap/osprey

Windows

scoop bucket add nimblesite https://github.com/Nimblesite/scoop-bucket
scoop install osprey

Build something practical

Start in the playground, follow the documentation or explore the compiler on GitHub.