Build the Future with Osprey
A modern functional language with compile-time effect safety, lightweight fiber concurrency, and immutable persistent collections. Compiles to LLVM.
// Simple, clean function definitions
fn double(n: int) -> int = n * 2
// String interpolation that works
let x = 42
let name = "Alice"
print("x = ${x}")
print("name = ${name}")
// Pattern matching on values
let result = match x {
42 => "The answer!"
0 => "Zero"
_ => "Something else"
}
print("Result: ${result}")
Get Osprey Now
🍎 macOS / 🐧 Linux
brew install nimblesite/tap/osprey
🪟 Windows
scoop bucket add nimblesite https://github.com/Nimblesite/scoop-bucket
scoop install osprey
🌐 Try Web Compiler
No installation needed! Compile and run Osprey code in your browser.
Open Playground🔨 Build from Source
Want the latest features? Build the compiler locally from GitHub.
View on GitHubThe Vision
Osprey was born from the belief that elegance is the best defence against bugs. Too many hours are wasted debugging null pointer exceptions, handling unexpected panics, and tracking down race conditions. Osprey is different.
Osprey's type system aims to prevent entire classes of bugs at compile time, make concurrency safe by default, and keep your code maintainable for years to come.
// Effects in the type, handlers at the edge
effect Logger { log: fn(string) -> Unit }
fn greet(name: string) -> Unit !Logger =
perform Logger.log("Hello, ${name}!")
handle Logger
log msg => print(msg)
in greet("Alice")
Language Features
Type-Safe & Expressive
Strong static typing prevents runtime errors while keeping syntax clean and readable. Expression-bodied functions eliminate boilerplate.
- Explicit type annotations
- Compile-time error checking
- Self-documenting code
- Expression-bodied functions
fn analyzeNumber(n: int) -> string = match n {
0 => "Zero"
42 => "The answer!"
_ => "Something else"
}
fn double(x: int) -> int = x * 2
fn square(x: int) -> int = x * x
// Test the functions
print("Testing functions:")
print(analyzeNumber(0))
print(analyzeNumber(42))
fn getGrade(score: int) -> string = match score {
100 => "Perfect!"
95 => "Excellent"
85 => "Very Good"
75 => "Good"
_ => "Needs Improvement"
}
// Test the function
print("Grade for 100: ${getGrade(100)}")
print("Grade for 95: ${getGrade(95)}")
Pattern Matching
Elegant pattern matching with exhaustiveness checking ensures you handle all cases safely.
- Exhaustive pattern checking
- Safe value destructuring
- Clear conditional logic
- No forgotten edge cases
String Interpolation
Built-in string interpolation with full expression support makes output formatting clean and readable.
- Expression interpolation
- Type-safe formatting
- Readable string templates
- No manual concatenation
// String interpolation example
let name = "Alice"
let age = 25
let score = 95
print("Hello ${name}!")
print("Next year you'll be ${age + 1}")
print("Double score: ${score * 2}")
print("${name} (${age}) scored ${score}/100")
// Functional Programming Example
fn double(x: int) -> int = x * 2
fn square(x: int) -> int = x * x
// Clean data transformations
5 |> double |> square |> print
// Range operations with forEach
print("Range operations:")
range(1, 10) |> forEach(print)
Functional Programming
Pipe operators and functional iterators create elegant data processing pipelines.
- Pipe operator for data flow
- Functional iterators
- Immutable by default
- Clean transformation chains
Algebraic Effects
Osprey is the world's first language with 100% compile-time effect safety. Side effects live in a function's type, and an unhandled effect is a compilation error — never a runtime surprise.
Effects in the type, handlers at the edge
Declare an effect, perform its operations, then swap behaviour by changing the
handle … in block — no globals, no dependency injection framework. The same logic runs
against a production handler, a test double, or a silent one.
- Unhandled effects fail at compile time
- Handlers are first-class and composable
- Nested handlers override outer ones
- Pairs naturally with fibers, HTTP, and TUIs
effect Logger {
log: fn(string) -> Unit
}
fn greet(name: string) -> Unit !Logger =
perform Logger.log("Hello, ${name}!")
// Production: write to stdout
handle Logger
log msg => print(msg)
in greet("Alice")
// Test: stay silent — same code, new handler
handle Logger
log msg => 0
in greet("Bob")
Persistent Collections
// List — 32-way bitmapped vector trie
let xs = listAppend(listAppend(List(), 10), 20)
let ys = listAppend(xs, 99)
listLength(xs) // 2 — xs untouched
listLength(ys) // 3
xs + ys // O(n+m) concat
// Map — Hash Array Mapped Trie (HAMT)
let m = mapSet(Map(), "alice", 25)
let m2 = mapSet(m, "bob", 30)
mapContains(m, "bob") // false
mapContains(m2, "bob") // true
m + m2 // right-biased union
Built on FP foundations
Immutable List<T> and Map<K, V> backed by structural sharing, so "modifying" a collection never copies the whole thing — old versions stay valid in O(1) extra space relative to the change.
- List: 32-way bitmapped vector trie (Bagwell 2000; Clojure's PersistentVector). O(log₃₂ n) point ops.
- Map: Hash Array Mapped Trie with bitmap-packed children. O(log₃₂ n) expected for lookup, insert and remove.
+operator dispatches tolistConcatfor lists andmapMerge(right-biased) for maps.- Backed by 33 C-level assertions and 12 byte-exact end-to-end test programs.
Compile-Time Effect Safety
Every side effect is tracked in the type system. Unhandled effects are caught by the compiler.
Memory Safe
Strong static typing and immutable data prevent buffer overflows and data races.
Compiles to LLVM
Stream fusion and structural sharing lower high-level code to efficient native binaries.
Real-World Examples
Fiber Concurrency
fn work(n: int) -> int = n * n
// Spawn lightweight fibers, await out of order
let a = spawn work(6)
let b = spawn work(7)
let rb = await(b)
let ra = await(a)
print("a=${ra}, b=${rb}")
// Message passing over a channel
let ch = Channel(1)
send(ch, 42)
print("got ${recv(ch)}")
C Interop & SQLite
// @link: sqlite3
// Bind C functions with typed signatures
extern fn sqlite3_open(path: string, ppDb: Ptr) -> int
extern fn osprey_ffi_cell() -> Ptr
extern fn osprey_ffi_deref(cell: Ptr) -> Ptr
// Ptr carries the opaque C handle — no
// arithmetic, no dereference, handles only
let cell = osprey_ffi_cell()
let rc = sqlite3_open("app.db", cell)
let db = osprey_ffi_deref(cell)
print("sqlite open rc = ${rc}")
HTTPS Client
// TLS via OpenSSL — https just works
let client = httpCreateClient("https://httpbin.org", 5000)
let status = httpGet(client, "/get", "")
print("status: ${status}")
let closed = httpCloseClient(client)
print("client closed")
Core Philosophy
Referential Transparency
Functions return the same output for the same input. Side effects are explicit and tracked by the type system.
Immutability by Default
Data is immutable unless explicitly marked mutable, making it safe to share across fibers.
Explicit Error Handling
No hidden exceptions or panics. All failures return Result types you must handle.
Zero-Cost Abstractions
High-level features — effects, iterators, persistent collections — compile to efficient native code.
How Osprey Is Different
| Feature | Traditional | Osprey |
|---|---|---|
| Side Effects | Untracked, implicit | In the type; unhandled effects are a compile error |
| Error Handling | Exceptions, panics | Result types |
| Null Safety | Null pointer exceptions | Option types only |
| Concurrency | Shared mutable state | Fiber-isolated, message-passing |
| Type Safety | Runtime type errors possible | Compile-time prevention via Hindley-Milner inference |
| Memory Management | Manual memory or garbage collection | Memory-safe, no GC pauses |
Help Build the Future of Programming
Anyone can contribute. AI assistants like Claude + Cursor make compiler development accessible to regular developers. No CS degree required.