Easy readable, fully async.
Fast for computer, fast for developer,
efficient for LLMs.

Nostos (νόστος) — a hero's homecoming

Start Tutorial Learn More

Getting Started

macOS (Homebrew)

brew tap pegesund/nostos
brew install nostos
nostos --version

macOS (Manual Download)

# For Apple Silicon (M1/M2/M3)
curl -LO https://github.com/pegesund/nostos/releases/latest/download/nostos-v0.2.1-aarch64-apple-darwin.tar.gz
tar -xzf nostos-v0.2.1-aarch64-apple-darwin.tar.gz

# For Intel Mac
curl -LO https://github.com/pegesund/nostos/releases/latest/download/nostos-v0.2.1-x86_64-apple-darwin.tar.gz
tar -xzf nostos-v0.2.1-x86_64-apple-darwin.tar.gz

# Make executable and move to PATH
chmod +x nostos
sudo mv nostos /usr/local/bin/

# If macOS blocks it, allow in System Preferences > Security & Privacy
# Or run: xattr -d com.apple.quarantine nostos

Linux

# Download and extract
curl -LO https://github.com/pegesund/nostos/releases/latest/download/nostos-v0.2.1-x86_64-unknown-linux-gnu.tar.gz
tar -xzf nostos-v0.2.1-x86_64-unknown-linux-gnu.tar.gz

# Make executable and move to PATH
chmod +x nostos
sudo mv nostos /usr/local/bin/

# Verify installation
nostos --version

Windows

# Download from GitHub Releases
# https://github.com/pegesund/nostos/releases/latest

# Or with PowerShell:
Invoke-WebRequest -Uri "https://github.com/pegesund/nostos/releases/latest/download/nostos-v0.2.1-x86_64-pc-windows-msvc.zip" -OutFile "nostos.zip"
Expand-Archive -Path "nostos.zip" -DestinationPath "."

# Add to PATH or run directly
.\nostos.exe --version

Your First Program

# Create hello.nos
echo 'main() = println("Hello, Nostos!")' > hello.nos

# Run it
nostos hello.nos

# Or start the interactive REPL
nostos repl

First run extracts the standard library and builds a cache (~1 second). Subsequent runs start in ~0.1 seconds.

Code That Reads Like Poetry

examples.nos
# Pattern matching flows naturally
fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)

# Lists destructure elegantly
sum([]) = 0
sum([head | tail]) = head + sum(tail)

# Quicksort in 3 lines
quicksort([]) = []
quicksort([pivot | rest]) =
    quicksort(rest.filter(x => x < pivot)) ++ [pivot] ++ quicksort(rest.filter(x => x >= pivot))

# Pipes chain beautifully
result = users
    .filter(u => u.active)
    .map(u => u.name)
    .join(", ")

Pattern Matching Rocks

Describe the shape you expect, not the steps to extract it. The compiler ensures you handle every case.

tree.nos
# A binary tree: either empty or a node with value and children
type Tree[T] = Empty | Node(T, Tree[T], Tree[T])

# Count nodes - pattern matching makes it trivial
size(Empty) = 0
size(Node(_, left, right)) = 1 + size(left) + size(right)

# Sum all values
sum(Empty) = 0
sum(Node(val, left, right)) = val + sum(left) + sum(right)

# Search with guards
contains(Empty, _) = false
contains(Node(val, _, _), target) when val == target = true
contains(Node(_, left, right), target) =
    contains(left, target) || contains(right, target)

Non-Blocking by Default

Every I/O operation yields automatically. No async, no await, no colored functions. Your code looks synchronous but runs concurrently.

concurrency.nos
# These HTTP requests run in parallel—no special syntax needed
fetchAll(urls) = urls.map(url => Http.get(url))

# Spawn 100,000 processes without breaking a sweat
main() = {
    pids = range(1, 100001).map(i => spawn(() => worker(i)))
    println("Spawned " ++ show(pids.length()) ++ " processes")
}

Thread-Safe Shared State

Use mvar for globals that multiple processes can safely read and write. No manual locks needed.

counter_server.nos
# Thread-safe global counter - all processes can safely update it
mvar requestCount: Int = 0
mvar activeUsers: List[String] = []

handleRequest(req) = {
    requestCount = requestCount + 1  # Atomic increment
    activeUsers = activeUsers ++ [req.user]  # Safe list append
    respondText(req, "Request #" ++ show(requestCount))
}

main() = {
    # Each request spawns a new process, all share the mvars safely
    serve(8080, handleRequest)
}

Functional First, Pragmatism Always

Nostos is designed to save you time. We prefer functional patterns because they're often clearer and safer, but we won't force you into contortions when a mutable variable just makes sense.

Massive Concurrency

Spawn hundreds of thousands of lightweight processes. Each is ~2KB overhead. Message passing beats shared memory and locks.

Immutable by Default

Data structures use structural sharing—fast and safe. Use var when a mutable variable makes your intent clearer.

Type Inference

Hindley-Milner inference means you rarely write types, but the compiler catches errors at compile time. Generics and traits work seamlessly.

JIT Compilation

Register-based VM with Cranelift-powered JIT. Hot paths compile to native code.

Living REPL

Live reload, autocomplete, inline errors, state inspection. The REPL isn't just for experiments—it's your development cockpit.

VS Code Integration

Real-time error checking, go to definition, smart autocomplete with type inference, integrated REPL in your editor.

PostgreSQL

Query your database with minimal friction. No ORM, just plain queries and safe types.

database.nos
type User = { name: String, email: String }

main() = {
    conn = Pg.connect("host=localhost dbname=mydb user=postgres")

    # Parameterized queries prevent SQL injection
    rows = Pg.query(conn, "SELECT name, email FROM users WHERE age > $1", 18)

    # Or map directly to typed records
    users: List[User] = query[User](conn, "SELECT name, email FROM users", ())
    users.map(u => println(u.name ++ ": " ++ u.email))
}

Connection pooling, transactions, prepared statements, pgvector, LISTEN/NOTIFY, TLS

HTTP Server & Client

Production-ready networking with automatic keep-alive and TLS.

server.nos
# Server
handle(req) = match req.path {
    "/api/users" -> jsonResponse(getUsers())
    "/health" -> textResponse("OK")
    _ -> notFound()
}

main() = Server.start(8080, handle, workers: 8)

# Client
response = Http.get("https://api.example.com/data")
data = Json.parse(response.body)

Traits for Polymorphism

Define shared behavior without class hierarchies.

traits.nos
trait Drawable
    draw(self) -> String
end

type Circle = { radius: Float }
type Square = { side: Float }

Circle: Drawable
    draw(self) = "●"
end

Square: Drawable
    draw(self) = "■"
end

# Works with any Drawable
render[T: Drawable](shapes: List[T]) = shapes.map(s => s.draw()).join(" ")

Modules and Imports

Organize code into modules. The compiler catches name conflicts at compile time.

imports.nos
# Import everything from a module
use math.*

# Import specific items
use stdlib.server.{serve, respondText}

# Import with aliases to avoid conflicts
use graphics.{draw as graphicsDraw}
use text.{draw as textDraw}

# If two modules export the same name, the compiler catches it:
# Error: import conflict: `helper` is imported from both `a` and `b`
# Help: use qualified name `b.helper` or selective import

Reactive Web (RWeb)

Full-stack reactive apps. Server-side rendering, automatic DOM diffing. Change state, UI updates.

todo_app.nos
use stdlib.rweb.*

reactive Todo = { text: String, done: Bool }
reactive State = { todos: List[Todo] }

sessionSetup(writerId) = {
    state = State([])

    renderPage = () => RHtml(div([
        h1("Todo App"),
        ul(state.todos.map(t => li(t.text))),
        input(type: "text", dataAction: "add")
    ]))

    onAction = (action, params) => match action {
        "add" -> { state.todos = state.todos ++ [Todo(params.text, false)] }
        _ -> ()
    }

    (renderPage, onAction)
}

main() = startRWeb(8080, "Todos", sessionSetup)

And More...

WebSockets

Real-time bidirectional communication with TLS support

Reactive Web (RWeb)

Full-stack reactive apps with server-side rendering

JSON

Parse, generate, and transform JSON data

File I/O

Async file operations with streaming

Cryptography

SHA-256/512, bcrypt, secure random

Regular Expressions

Fast pattern matching, guaranteed linear time

TCP Sockets

Low-level networking when you need it

HTML Templating

Type-safe templates with named parameters

Getting Started

terminal
# Clone and build
git clone https://github.com/pegesund/nostos.git
cd nostos
cargo build --release

# Run the REPL
./target/release/nostos

# Run a program
./target/release/nostos examples/hello_server.nos

# Install VS Code extension
cd editors/vscode && npm install && npm run package
code --install-extension nostos-*.vsix

Philosophy

Readable code is maintainable code. Pattern matching, clean syntax, and functional patterns make code self-documenting. If an LLM can read it, so can your future self.

Concurrency without complexity. No async, no await, no colored functions. Spawn a million lightweight processes. Write sequential code that runs in parallel.

Shared state without tears. Use mvar for thread-safe globals. Multiple processes read and write atomically—no manual locks, no race conditions, no headaches.

Functional by default, pragmatic when needed. Immutable data structures prevent bugs. But when a var or a while loop is clearer, use it without guilt.

Types that work for you. Hindley-Milner inference means you rarely write types. The compiler catches errors; you focus on logic.

From prototype to production. The same language for scripting, web servers, and data processing. PostgreSQL, HTTP, WebSockets—built in, not bolted on.

Built on giants. Tokio for I/O, Cranelift for JIT, Rust for reliability. We don't reinvent wheels—we assemble the best ones.

AI-Friendly Language Reference

Use our comprehensive language reference as a skill for AI coding assistants like Claude, Cursor, or GitHub Copilot. One file, complete documentation.

View Language Reference