Safety
Algebraic, with no panics. Failures are Result values the caller must handle, matching is exhaustive, integer arithmetic follows a stated Arith policy, and effects make outside work visible and replaceable.
A functional language built to minimise bugs and failures: algebraic effects and Result instead of panics, inferred types that say more with less text, and native LLVM compilation.
effect Drafts { save: fn(string, string) -> Result<int, Error> }
fn saveDraft(path, text) = match perform Drafts.save(path, text) {
Success _ => "Saved"
Error { message } => "Retry: ${message}"
}
fn onSave() = saveDraft("draft.txt", "Ship it.")
let disk = handler Drafts {
save path text => writeFile(path, text)
}
print(disk(onSave))
effect Drafts
save : (string, string) => Result<int, Error>
saveDraft path text = match perform Drafts.save path text
Success _ => "Saved"
Error message => "Retry: ${message}"
onSave () = saveDraft "draft.txt" "Ship it."
disk = handler Drafts
save path text => writeFile path text
print (disk onSave)
# osprey: flavor=python
@effect
class Drafts:
def save(path: str, text: str) -> Result[int, Error]: ...
def saveDraft(path, text):
match perform(Drafts.save(path, text)):
case Success():
return "Saved"
case Error(message=message):
return f"Retry: {message}"
def onSave():
return saveDraft("draft.txt", "Ship it.")
@handler(Drafts)
class disk:
def save(path, text):
return writeFile(path, text)
def main():
print(disk(onSave))
Three syntax flavors: three ways to write Osprey.
01 / Built with Osprey
brew install nimblesite/tap/osprey
Getting started guide
Windows uses Scoop. Native compilation requires clang (LLVM 15+). Or try Osprey online ↗
02 / Three pillars
Osprey exists to minimise bugs and failures, to express more program with less text, and to run natively. Every feature on this page serves one of the three.
Algebraic, with no panics. Failures are Result values the caller must handle, matching is exhaustive, integer arithmetic follows a stated Arith policy, and effects make outside work visible and replaceable.
Inferred types, effects instead of plumbing, and no separate async form. Less text means fewer tokens: programs are cheaper for AI agents to read and write, and easier for people to hold in their heads.
Compare the syntax flavors ↗Native binaries through LLVM with no VM or JIT warm-up. Static handler selection removes runtime effect dispatch, fibers are lightweight, and the memory backend is a per-build choice.
See the benchmarks ↗03 / Operations and handlers
effect declares an operation’s arguments and result type. perform requests the operation; a handler supplies the implementation.
Choose storage once: disk(onSave) supplies it for onSave and the saveDraft helper it calls. Neither function takes a storage object. Replace the handler in a test to simulate a failure; the application logic stays the same.
The compiler checks operation types and rejects requests without a matching handler, including requests made through helper functions.
See effect and handler syntax ↗Osprey is actively evolving. Native control effects support single-shot resumption; full scoped effect rows and reusable continuations remain in development. Current support →
04 / Types and effects
Inferred static types, immutable data, and exhaustive pattern matching. Expected failures remain visible as Result values.
A syntax flavor is a way to write the same Osprey language: Default uses braces, ML uses indentation, and Python flavor uses Python-style syntax. Choose per file and import across flavors; all share one type system, effects and compiler.
Compare the syntax flavors ↗Native fibers and channels keep concurrent work in ordinary functions. Choose the default allocator, tracing GC, or reference counting for your native build.
Follow the fibers ↗On the host, an Arith handler supplies the integer overflow policy. Wrapping and saturating helpers state their behavior in the call.
05 / Deployment targets
Compile native programs for macOS, Linux and Windows, WebAssembly for browsers and WASI, or shared application logic for iOS and Android.
Direct executables. C interop. A choice of memory backends.
--target=native
02 / BROWSER & WASIShared application logic, connected to the browser through a host.
--target=wasm32
03 / IN YOUR HANDOsprey modules behind SwiftUI and native Android interfaces.
--target=ios / android-arm64
Build for the capabilities of your target. Unsupported dynamic control effects are rejected on WebAssembly and mobile.
Real measurements, open benchmarks ↗06 / Python flavor and tensor handlers
Keep def, lambda, and f-strings. Port computation into Osprey’s Python syntax flavor with inferred types and checked effects. Keep Python libraries through explicit interop.
GPT-2 is the worked port: one Tensor effect, a CPU handler, and a GPU handler using Metal or OpenCL. The handler decides where the arithmetic runs.
# osprey: flavor=python
def product(a, b, rows, inner, cols):
"""a [rows, inner] times b [inner, cols]; the handler aborts on failed work."""
return perform(Tensor.mat_mul(a, b, rows, inner, cols))
Python flavor is Osprey source; porting requires adapting Python’s dynamic behavior. Shared tensor storage across Python interop is planned. GPU kernels are not fused; speed depends on the workload and backend. Measurements and source →
07 / Shared modules, native hosts
Issue Inbox puts its model, updates, UI layout, GitHub decoding, and SQLite commands in shared Osprey modules. SwiftUI and Android widgets render the application.
Actual simulator and emulator captures. Also verified on a physical iPhone. Native hosts provide rendering, SQLite, HTTPS, and lifecycle services.
08 / Community
Ask questions, share what you are building, and talk language design with the people making Osprey.