Two Weeks That Changed Everything: A Luma Progress Report

Two Weeks That Changed Everything: A Luma Progress Report

February 15, 2026·
Luma Core Team

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.

This is what happened in fourteen days.

The features

Web server and routing

Luma now has a built-in HTTP server. You define routes with get(), post(), put(), and delete(), and respond with text(), html(), json(), or redirect(). No framework, no dependency — just a few functions that compose naturally.

get("/hello", (req) -> {
    return text("Hello, ${req.query["name"]}")
})

server.start(ServerConfig { port: 8080 })

This is the feature that turned Luma from “a language you learn from” into “a language you build with.”

JSON without ceremony

Parsing and generating JSON is a one-liner. json.decode() returns typed maps and lists. json.encode() turns them back into strings. json.pretty() formats them for humans.

data: {str: int} = json.decode("{\"users\": 42}")
output: str = json.pretty(data)

No struct tags, no annotations, no serialization framework. Data in, data out.

Date and time

Three purpose-built types — datetime, date, and time — with creation, extraction, formatting, and parsing. They exist because stringly-typed timestamps are a category of bug, not a design choice.

now: datetime = datetime.now()
print(now.format("2006-01-02 15:04:05"))

Binary buffers

The buffer type gives Luma a proper way to work with binary data. Sequential reads, writes, cursor management, and conversion to/from [byte]. This is the foundation for protocol parsing, file format handling, and anything that operates below the string level.

buf: buffer = buffer()
buf.write("Hello")
buf.write([0x0A])
print(buf.len())   // 6

Cryptography

Six hash algorithms (MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512), HMAC-SHA256 and HMAC-SHA512, cryptographic random bytes, and constant-time comparison. All under the crypto namespace, all returning [byte] that you can encode with .to_hex() or .to_base64().

hash: [byte] = crypto.sha256("secret".to_bytes())
print(hash.to_hex())

These aren’t convenience wrappers — they’re the primitives you need for authentication tokens, password hashing, and data integrity checks.

TCP networking

Raw socket connections with tcp.connect(), read/write operations, TLS support, and STARTTLS upgrade. Enough to implement protocol clients from scratch.

conn: tcp = tcp.connect("example.com", 443, {"tls": true})
conn.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
response: str = conn.read("\r\n")
conn.close()

Database connectivity

MySQL support landed with a query interface that returns typed results. Parameterized queries, connection management, and result iteration — the minimum viable surface for data-driven applications.

CSV module

A file-based module written entirely in Luma, proving that the module system works for real library code. Reading, writing, and parsing CSV data with proper quoting and delimiter handling.

Error handling

This was the big design decision. No exceptions, no try/catch, no Result<T, E>. Instead: error("message") raises a recoverable error, and .or() catches it inline.

result: int = divide(10, 0).or(0)

One line. No new control flow. The same dot-chaining syntax you already know. When you need the error message, a lambda form gives you access:

result: int = divide(10, 0).or(err -> {
    print("Failed: ${err}")
    return -1
})

For propagation, try forwards errors to the caller. For unrecoverable situations, panic() crashes immediately. Four tools, each with a clear purpose.

Closures and anonymous functions

Lambdas with variable capture. Three forms — zero-parameter () -> expr, single-parameter x -> expr, and multi-parameter (a, b) -> expr — all using the same arrow syntax. Expression bodies for one-liners, block bodies for complex logic. Full closure support with captured variables from the enclosing scope.

threshold: int = 10
big: [int] = numbers.filter(x -> x > threshold)

This completes the functional programming story. Higher-order functions, callbacks, factories, and data pipelines all work naturally now.

Pattern matching

match with value patterns, variable binding, wildcard _, and guard clauses. Match works as both a statement and an expression — and it handles optionals cleanly:

result: ?int = "42".to_int()

match result {
    nil -> print("parse failed")
    n -> print(n)
}

Regex string methods

Five methods on every string: regex_match(), regex_find(), regex_find_all(), regex_replace(), and regex_split(). All take a pattern string, all follow existing Luma conventions. No separate regex type to manage.

clean: str = input.regex_replace("\\s+", " ")
parts: [str] = line.regex_split("[,;]\\s*")
found: ?str = text.regex_find("[0-9]+")

What this adds up to

Here’s the feature count before and after these two weeks:

Category Feb 1 Feb 15
Primitive types 5 5
Collection types 3 3
Special types 3 (file, cli, Error) 9 (+buffer, tcp, datetime, date, time, buffer)
String methods ~20 30+ (with regex)
Core library modules 2 (file, cli) 7 (+json, crypto, tcp, datetime, csv)
Error handling panic only error/try/panic/.or()
Closures No Full support
Pattern matching No match with guards
Networking No TCP + TLS + HTTP server
Database No MySQL
Playground examples ~30 75
Documentation pages ~40 80

The language didn’t just gain features. It crossed a threshold. Before this sprint, Luma was a teaching language. Now it’s a tool you could use to build a small web service, a CLI utility, or a data processing pipeline — and the code would actually read well doing it.

Documentation

We also did a full audit of every documentation page. Lists, maps, sets, optional types, and type conversion were all rewritten to match what the compiler actually implements. Features that existed only in docs but not in code were removed. Features that existed in code but not in docs were added. The documentation now tells the truth.

This matters more than it sounds. A language with wrong docs is worse than a language with no docs. Wrong docs teach people to write code that doesn’t compile, and then they blame themselves instead of the documentation.

How far from first release?

Honest assessment. Luma has a working compiler, a working standard library, a working playground, comprehensive documentation, and enough features to build real programs. The syntax is stable. The type system is consistent. The error handling model is final.

What’s still missing for a proper v0.1:

Must-have:

  • Installable binary — users need luma run file.luma without cloning the repo and running go run
  • Proper error messages — line numbers, file names, and clear descriptions when compilation fails
  • For loopswalk covers iteration, but a traditional for i in 0..n loop is expected by most developers
  • Standard library polish — some methods exist as .lcore helpers but aren’t wired up in the compiler (list .index_of(), .get(), etc.)

Should-have:

  • Package manager or at least a luma.toml project file
  • REPL — interactive exploration is important for a new language
  • Testing framework — even minimal assert support would help
  • Better string formattingsprint() works, but a proper format function would be cleaner

Nice-to-have:

  • LSP server — editor support makes a huge difference in developer experience
  • More database drivers — PostgreSQL, SQLite
  • HTTP client — TCP is there, but a high-level http.get() would round things out

The must-have list is short and concrete. Nothing on it requires rethinking the language design — it’s packaging, polish, and filling known gaps.

If the last two weeks are any indication of pace, Luma could have a downloadable v0.1 within a month. That’s not a promise. It’s a trajectory.

What’s next

The immediate priority is the installable binary and error messages. A language people can’t install is a language people won’t try. And when they do try it, the first thing they’ll hit is a compiler error — and that error needs to actually help them.

After that: fill the gaps in the standard library, add for loops, and ship v0.1.

We’ll keep posting here as things ship. If you want to follow along or try the playground, everything lives at luma.tarkvara.ee .

Last updated on