Luma Blog

When Two Modules Collide: How Luma Learned Namespace Isolation

We hit a wall this week that every growing language eventually hits. Two modules, both perfectly valid on their own, crashed into each other the moment someone imported them into the same program. The fix taught us something important about how Luma should think about module boundaries. The bug nobody writes on purpose Here’s the setup. You write a math utility module: // import2.luma fn helper() -> int { return 42 } pub fn calc2(a: int) -> int { return helper() + a } And a separate statistics module:

Read more →

February 16, 2026

SQLite Driver Under Development: Pure Luma Reads SQLite Files

We’re building a SQLite driver for Luma. It’s not released yet, but the core is working and we want to share where we are, what’s implemented, and what we learned along the way. Like the MySQL driver, the SQLite driver is written entirely in Luma. No C library. No Go wrapper. No external dependencies. Just .luma code that reads SQLite database files byte by byte, using the same primitives every Luma developer has access to: file, buffer, bit, and the byte conversion methods we already shipped.

Read more →

February 16, 2026

HTTP Client Lands: Luma Talks to the Web

Every modern program talks to an API. Weather data, payment gateways, authentication services, third-party integrations — HTTP is the lingua franca of the internet. Luma could already serve HTTP with server.*, but it couldn’t make requests. That gap closed today. Luma now has a built-in HTTP client with a builder pattern: create a request, configure it step by step, execute. The simplest case res = http("https://api.example.com/users").get() print(res.status) // 200 print(res.body) // JSON string One line to make a GET request. http() creates the client, .get() fires it. The response gives you status, body, and headers — everything you need.

Read more →

February 16, 2026

UUIDs Land in Luma: Generate, Validate, No Dependencies

UUIDs are everywhere. Database primary keys, API request IDs, session tokens, distributed system identifiers, file names that need to be globally unique — they’re one of the most used primitives in modern software. And until today, generating one in Luma meant pulling in an external module or cobbling together random bytes by hand. Not anymore. Luma now has a built-in uuid namespace with three functions that cover the real-world cases.

Read more →

February 15, 2026

Randomness Without the Setup: Luma Gets random.int(), random.float(), and random.bool()

Every general-purpose language needs randomness. Dice rolls, coin flips, shuffling, sampling, test data, procedural generation — random numbers show up constantly in everyday programming. Until today, the only randomness in Luma was crypto.rand(n), which generates cryptographically secure random bytes. Great for tokens and security. Not great when you just want a number between 1 and 6. Now Luma has a random namespace with three functions that cover the common cases.

Read more →

February 15, 2026

Two Weeks That Changed Everything: A Luma Progress Report

Two weeks ago, Luma could define structs, call functions, iterate over lists, and write to files. It was a language with a foundation — solid types, clean syntax, and a compiler that produced real Go code. But if you wanted to build something you’d actually use, you’d hit walls fast. No networking. No JSON. No error handling. No way to talk to a database or parse a date. Today, you can build a REST API, hash passwords, query MySQL, parse CSV files, match patterns, and handle errors — all in the same language, with the same syntax philosophy that’s been there from the start.

Read more →

February 15, 2026

Pattern Matching Arrives in Luma: Match Your Way to Cleaner Code

If you’ve written more than a few dozen lines of Luma, you’ve written an if/else chain you weren’t proud of. Something like this: if status == "active" { print("user is active") } else if status == "banned" { print("user is banned") } else if status == "pending" { print("waiting for approval") } else { print("unknown status") } Four branches. The variable status repeated four times. The logic is simple, but the code makes it look complicated.

Read more →

February 14, 2026

Closures and Anonymous Functions: The Glue That Holds Luma Together

Most languages have closures. Few make them feel like they belong. In a lot of languages, anonymous functions are bolted on. They require special keywords, awkward syntax, or ceremony that makes them feel like a different dialect from the rest of the code. You end up with function(x) { return x + 1; } sitting inside a method call that was otherwise clean and readable. The cost isn’t just keystrokes — it’s cognitive overhead. Every time you see the syntax, you have to context-switch.

Read more →

February 14, 2026

Error Handling Lands in Luma: No Try/Catch, Just .or()

Every language eventually needs to answer a question: what happens when something goes wrong? Some languages answer with exceptions and try/catch blocks. Others use result types and pattern matching. A few punt the question entirely and leave you with null checks or error codes. Each approach has trade-offs, but they all share a common problem — they introduce new control flow that doesn’t look like anything else in the language.

Read more →

February 13, 2026

What Building a CSV Module Taught Us About Our Own Language

We recently set out to build a CSV module for Luma — a pure .luma file that can parse, query, and convert comma-separated data. The kind of practical, everyday library that any language should be able to support. What we didn’t expect was how quickly it would expose gaps in the compiler itself. This is a story about building something real and letting it tell you what’s missing. The Plan The idea was straightforward: represent a CSV table as [[str]] — a list of rows, where each row is a list of strings. The first row is the header. Simple functions for reading, filtering, converting. No special types, no framework — just lists and strings.

Read more →

February 12, 2026

Luma Talks to Databases: The Full Stack Lands

Four blog posts ago, we asked a question: how should Luma talk to MySQL? We had no TCP sockets. No binary buffers. No crypto. No database interface. We had a vision and a plan — build the toolbox first, then build a driver with those tools. Today, every piece of that plan is done. Luma has a complete database stack — from raw TCP sockets all the way up to conn.query() with named parameters, transactions, and debug output. And the MySQL driver that ties it all together is written entirely in Luma. Not in Go, not in C, not hidden inside the compiler. In Luma. About 800 lines of it.

Read more →

February 11, 2026

TCP Lands: Luma Opens a Socket to the World

Three tools. Three posts. Today, the last one lands. We shipped binary buffers for building and parsing packets. We shipped crypto for hashing and authentication. Now Luma gets TCP networking — the transport layer that connects everything to everything else. With TCP, a Luma program can open a socket, send bytes, read responses, upgrade to TLS, and close the connection. That’s enough to speak any protocol: MySQL, PostgreSQL, Redis, SMTP, HTTP, or anything else that runs over TCP.

Read more →

February 11, 2026

Crypto Arrives: Hashing, HMAC, and the Primitives That Unlock Everything

Two tools down, one to go. Yesterday we shipped binary buffers. Today, Luma gets a crypto module — hashing, HMAC, secure random generation, encoding, and the byte utilities that tie them together. These aren’t exotic features. They’re the primitives that show up the moment a program touches authentication, data integrity, API signatures, or tokens. Without them, you can’t verify a webhook, hash a password, generate a session token, or authenticate to a database. With them, all of that becomes straightforward.

Read more →

February 11, 2026

Binary Buffers Arrive: Luma Learns to Speak in Bytes

In our last post, we talked about building the toolbox that would make database drivers possible in pure Luma. We said TCP, binary buffers, and crypto were the foundation. Today, one of those pieces is done. Luma now has a buffer type — a first-class tool for building and parsing binary data. Why buffers matter Every network protocol speaks in bytes. MySQL encodes packet lengths as three little-endian bytes. DNS uses big-endian 16-bit integers. TLS, HTTP/2, WebSocket frames, Redis commands — they all define exact byte layouts that programs must construct and parse correctly.

Read more →

February 11, 2026

Thinking About Databases: How Should Luma Talk to MySQL?

Some features arrive with a clear design from the start. You know what the API should look like, you know how the pieces fit together, and the work is mostly about execution. Database support is not one of those features. Databases are one of the most requested capabilities for any practical language. If you can read files, serve HTTP, and parse JSON, the next question is always: can I query a database? And it’s a fair question. Most real-world programs eventually need to store or retrieve structured data, and MySQL and MariaDB remain among the most widely deployed databases in the world.

Read more →

February 10, 2026

Your First Real App: A REST API in 80 Lines of Luma

Over the past weeks, Luma has gained a web server, JSON support, file handling, and a module system. Each of those features made sense on its own. But the real test of a language isn’t whether individual features work - it’s whether they work together, in the kind of code people actually write. So we built a complete REST API. Not a toy. Not a hello-world with extra steps. A multi-file, persistent, CRUD application that manages users - with GET, POST, PUT, and DELETE endpoints, JSON request and response bodies, auto-assigned IDs, and data stored on disk.

Read more →

February 9, 2026

Dates and Times That Speak Human

Every application eventually deals with dates and times. A birthday, a deadline, a log entry, a schedule. It’s one of the most universal concepts in programming - and one of the most consistently painful. Some languages give you a single timestamp type and leave you to extract meaning from it. Others provide rich libraries that require you to learn a small language of format strings, locale objects, and timezone rules before you can print today’s date. In many cases, the default representation of a date is something like 2024-12-25T14:30:00Z - a format designed for machines, not for the person reading the code.

Read more →

February 9, 2026

JSON Without the Boilerplate: Luma Learns to Speak Data

Every modern program eventually talks to the outside world. It reads a configuration file. It calls an API. It saves state between runs. And almost always, that conversation happens in JSON. Most languages make this work, but few make it feel natural. You find yourself writing serializers, defining intermediate types, or importing libraries just to turn {"name": "Alice"} into something your program can actually use. It’s the kind of task that should take one line but somehow takes ten.

Read more →

February 9, 2026

Luma Web Server: Building Web Applications Made Simple

Luma now includes a built-in web server that lets you build HTTP applications with minimal boilerplate. True to Luma’s philosophy — handlers are just functions, types are clear and explicit, and you can go from zero to a running server in a few lines of code. Hello World fn hello(req: Request) -> Response { return text("Hello World!") } routes: [Route] = [get("/", hello)] config: ServerConfig = ServerConfig { port: 3000, routes: routes, } server.start(config) Run it:

Read more →

February 9, 2026

Command-Line Programs, Without the Ceremony

Command-line tools are often the first programs people write - and the ones they keep rewriting. They start small, grow organically, and over time accumulate flags, positional arguments, prompts, and little bits of glue code that are rarely fun to maintain. In many languages, building a CLI means choosing between two extremes. On one side, you have minimal access to raw arguments and standard input, which forces you to reinvent parsing and validation yourself. On the other, you have large frameworks that introduce their own abstractions, configuration files, and mental overhead before you can even read a flag.

Read more →

January 29, 2026