---

name: luan-programming

description: Work with code written in the Luan programming language or code in reactionary software projects. Use for any task involving Luan or `*.luan` files, or programming projects described as "reactionary".

---

# Luan programming

This skill is for writing, reviewing and debugging reactionary software: simple, reliable, usable software, as opposed to modern software, which is overcomplicated, bloated, unreliable and incomprehensible. Most of it is about Luan, the scripting language that reactionary websites are written in, but the principles apply to code in any language.

The people who use this skill know Franklin Schmidt (fschmidt), who wrote Luan. For help that this skill doesn't give, such as installing Java or getting a repository account, they can ask him at fschmidt@gmail.com.

## The principles come first

Read [references/principles.md](references/principles.md) before writing or reviewing code. Without it you will write modern code, because that is what most code you have seen looks like. In short:

- Simple means easy to understand, not short. Code that helps a reader is good; code that makes no sense must be removed, even if it does no harm to the computer.
- Nothing hidden. Every import is explicit, every callback is explicit, and there is no magic.
- Debugging must be easy. Fail fast, close to the cause.
- Judge an idea by how it works in practice, not by how popular it is. Most modern conventions are bad.
- If code becomes a struggle, stop and look for a simpler way instead of adding more.
- Do what is needed now, in a way that leaves room for later needs. Don't build for imagined needs.
- Fix a problem once, at its root, rather than patching it everywhere.

Follow the current practice of the reference projects consistently. Don't copy irregularities from older code.

## Getting the code

All reactionary projects are Mercurial repositories on RepositoryHosting, at `arkian.repositoryhosting.com`. Use `hg`, never git.

- Public repositories: `hg clone https://arkian.repositoryhosting.com/hg_public/arkian/<name>`
- With an account (needed for private repositories, and for pushing): `hg clone https://arkian.repositoryhosting.com/hg/arkian/<name>`, with the account in `~/.hgrc`:
	```
	[auth]
	arkian.prefix = https://arkian.repositoryhosting.com/hg/arkian/
	arkian.username = <username>
	arkian.password = <password>
	```
	Never put credentials in a repository.
- The public URLs sometimes fail with "HTTP 429 Too Many Requests", when the network's address is shared with too many others. Then don't keep retrying: ask the user for an account with access.
- To give the user changes, either commit and push if they want that, or make a patch: `hg add` new files, then `hg diff --git > name.patch`. They apply it with `hg import --no-commit`. Use `hg mv` to move files so history is kept, and `--git` so the patch records renames.

Clone these as reference whenever they apply:

- `luan`, for any Luan work. It is the final reference for how anything in Luan works. When unsure what a function does, or whether a function exists, read the source rather than guess (see "Finding things in the Luan source" below). It also holds the two reference sites below.
- `chat`, for any dynamic website, that is, one that stores data. It is a complete real site and the model for logins and for anything with users.

## Building and running Luan

- Luan needs Java 8 (a JDK, not just a JRE, because Luan compiles to Java). Later versions of Java are not supported.
- In the luan repository, `./build.sh` builds Luan and installs a `luan` command. Rebuild after pulling changes to `src/`. The build deletes the old build first, so a failed build leaves no `luan` at all.
- `luan file.luan` runs a script. `luan luan:http/serve.luan src` serves the site in `src/` at `http://localhost:8080/`; `luan luan:http/serve.luan src 8081` uses another port. Each project's `serve.sh` does this and logs to a file named `err`.
- Restart the server after changing Luan code, because loaded modules are cached.
- To test what you build, see [references/testing.md](references/testing.md).

## The reference projects

Copy from these. Each shows the current way of doing things.

- `luan/website` (www.luan.software): a static site, meaning pages generated by Luan but no stored data.
- `luan/examples/blog`: the smallest dynamic site, with posts stored in Lucene, search, new, edit and delete. It is the skeleton to start a new dynamic site from.
- `chat` (Luan Chat): a complete dynamic site, with users, logins by email, live updates, and email notifications. When a site has users, follow chat.

## How a Luan website is laid out

```
serve.sh                run the site locally
src/                    the site; URLs map to files here
	index.html.luan     the page for /
	X.html.luan         the page for /X.html
	X.js.luan           an AJAX handler for /X.js, which returns JavaScript
	site.css, site.js   static files, served as they are
	init.luan           optional; runs once when the site starts
	lib/                modules shared by pages: Shared.luan, Utils.luan, Db.luan, a module per kind of data
	private/            password protected when hosted; private/local/ holds data that is never pushed
```

A URL maps to `src/` plus the path plus `.luan`, so `/x/page.html` is `src/x/page.html.luan`. A file without `.luan` is served as it is. A page module returns a function, which the server calls for each request:

```lua
local Luan = require "luan:Luan.luan"
local error = Luan.error
local Io = require "luan:Io.luan"
local Http = require "luan:http/Http.luan"
local Html = require "luan:Html.luan"
local html_encode = Html.encode or error()
local Shared = require "site:/lib/Shared.luan"
local head = Shared.head or error()


return function()
	local name = Http.request.parameters.name
	Io.stdout = Http.response.text_writer()
%>
<!doctype html>
<html lang="en">
	<head>
<%		head() %>
		<title>Hello</title>
	</head>
	<body>
		<h1>Hello <%= html_encode(name or "world") %></h1>
	</body>
</html>
<%
end
```

- `%> ... <%` is a template: the text between them is written to `Io.stdout`, and `<%= expr %>` writes a value. So set `Io.stdout = Http.response.text_writer()` before the first template.
- Escape every value shown in HTML with `Html.encode`, including values from the request.
- Websites in detail: [references/websites.md](references/websites.md). Sites that store data: [references/dynamic-sites.md](references/dynamic-sites.md). HTML, CSS and JavaScript: [references/html-css-js.md](references/html-css-js.md).

## Luan in brief

Luan is like Lua 5.3, but simpler and stricter. Its full description is in the luan repository: `website/src/diff.html.luan` (how Luan differs from Lua) and `website/src/manual.html.luan` (the reference manual). What trips up someone who knows Lua:

- No globals. A chunk starts with only `require`, so every name is imported at the top of the file, and an undefined name is a compile error.
- Imports follow one pattern: `local Shared = require "site:/lib/Shared.luan"`, then `local head = Shared.head or error()`. The `or error()` fails at load time if the name is wrong, instead of producing a `nil` that fails later somewhere else. Use it for everything you import.
- The end of a line ends a statement. To continue a statement on the next line, end the line with `\`. A multi-line `or` chain needs `\` on every line but the last. In a table constructor, a new line separates fields, so no commas are needed.
- Conditions in `if`, `while` and `repeat`, and the operand of `not`, must be booleans. `if x then` with `x` a string is an error. `or` and `and` still work on any value: `options = options or {}`.
- Passing extra non-nil arguments to a function is an error.
- `..` converts every operand to a string, `nil` included, so logging never fails.
- No numeric `for`: write `for i in range(1,10) do`, with `local range = Luan.range or error()`. Likewise `ipairs`, `pairs` and `type` come from `luan:Luan.luan`. No `goto`, coroutines, `pcall`, `:` method calls, or automatic string-to-number conversion.
- `try ... catch e ... finally ... end` replaces `pcall`; `e` is an error table, and `e.message` is its message. `continue` exists. `x or error "message"` works as a statement.
- Blocks may end with `end_if`, `end_for`, `end_function` and so on instead of `end`. Use them in long nested code to make it readable and to catch block mistakes at compile time.
- `{nil}` is an empty list and `{}` an empty map. They behave the same except when converted to JSON, but write `{nil}` for a table meant as a list, to show the intent.
- Objects are tables of closures: `Post.new(post)` adds functions like `post.save()` to the table. There are no classes.
- Luan has real threads. An object becomes immutable once more than one thread can reach it, so application code never needs synchronization.
- Module URIs: `luan:X.luan` is the standard library, `site:/X` is the site's `src/` directory, and `file:`, `classpath:`, `http:` and others read other sources.

More in [references/luan.md](references/luan.md), and the standard library notes in [references/library/](references/library/), one file per module.

## Finding things in the Luan source

In the luan repository:

- `luan:X.luan` is `src/luan/modules/X.luan`. Most library functions are assigned from a Java class next to it: `String.find = StringLuan.find` means the code is in `src/luan/modules/StringLuan.java`. Follow the module's `.luan` file to the Java method.
- Luan can call any Java method with no glue code, so many library functions are thin layers over Java. Knowing the Java class often tells you how the function behaves.
- goodjava (`src/goodjava/`) is a Java library included in Luan, with simple replacements for the usual libraries: logger, parser, JSON, XML, RPC, Lucene query parser, webserver and mail.
- Test what a function does by running it: write a short script and run it with `luan`.

## How to work

- Read the code before changing it, and follow what the project already does.
- Test everything you build by running it: serve the site, open it in a headless browser, click through it, and watch for errors. Don't claim something works without having run it.
- When you find a problem, show it actually happening if you can. A failure you produced is convincing; a risk you can only describe usually isn't, and problems that cannot actually happen are noise.
- When a behavior might be intended, ask rather than call it a bug.
- Keep notes about the project for later sessions, in the project's `ai/notes.md`: what was decided, when, why and what it was meant to achieve; later, what actually happened, including ideas that were tried and removed. Then later sessions won't reopen settled questions or bring back removed ideas.
- For reviewing code, read [references/reviewing.md](references/reviewing.md).
