# The Luan language Luan is a scripting language implemented in Java, built on Ousterhout's idea of pairing a scripting language with a system language: Luan for applications, Java for libraries. It started as a Lua clone and was then changed according to the principles in [principles.md](principles.md). References in the luan repository: `website/src/diff.html.luan` ("How Luan differs from Lua"), `website/src/manual.html.luan` (the reference manual, which is unfinished and leaves out many library functions), and `website/src/tutorial.html.luan` (a short tutorial). The source in `src/luan/` is the final word, especially for the library. ## Differences from Lua - Simpler, mostly by removing things: no globals unless `_ENV` is defined (so an undefined name is a compile error), and no coroutines, `goto`, numeric `for`, `pcall`/`xpcall`, `:` method calls, call metamethod, proper tail calls, bitwise operators, or string-to-number coercion. Only tables have metatables. - Stricter: conditions in `if`, `while` and `repeat`, and the operand of `not`, must be booleans. Passing extra non-nil arguments to a function is an error. The end of a line ends a statement; a backslash at the end of a line continues it. - Additions: `try`/`catch`/`finally`, `continue`, optional block terminators (`end_do`, `end_for`, `end_function`, `end_if`, `end_try`, `end_while`), `or`/`and` expressions as statements, template statements, backticks, and a binary type. - `..` converts every operand to a string, so `"x="..nil` is `"x=nil"`. - In a table constructor, the end of a line separates fields. - `for` takes one expression, not a list: `for k, v in pairs(t) do`, `for i in range(1,10) do`. - Errors are tables. `Luan.error(message)` throws one; in `catch e`, `e.message` is the message. ## Values Values are Java objects. `nil` is Java `null`; booleans, numbers and strings are Java `Boolean`, `Number` and `String`; the binary type is `byte[]`. A number may be any Java `Number`, such as `Integer`, `Long` or `Double`, which matters when storing numbers in Lucene (see [dynamic-sites.md](dynamic-sites.md)). A `java` value is any other Java object. Strings can't hold binary data; that's what the binary type is for. `{nil}` is an empty list and `{}` an empty map. They behave the same except in `Parsers.json_string`, which writes `[]` for the first and `{}` for the second. Write `{nil}` for a table meant as a list, to show the intent. ## Statements that trip people up - One statement per line. Two statements on one line need `;` between them; without it, `Io.print("a") Io.print("b")` is "Invalid input". - Line ends: a statement split over lines needs `\` at the end of every line but the last. This includes a multi-line `or` chain: ```lua return starts_with(referer,"http://"..host.."/") \ or starts_with(referer,"https://"..host.."/") ``` - Logical statements: `x==5 or error "x should be 5"` is a statement. The last operand must itself be a statement, a function call or another logical expression ending in one, so `x==5 or "msg"` doesn't compile. The compiler reports this misleadingly, as "'end' expected" at that line. - Templates: `%>` starts literal text and `<%` ends it; `<%= expr %>` writes a value. The text goes to `Io.stdout`. A template can be any statement, so it can sit inside a loop or a function: ```lua for _, post in ipairs(posts) do %> <h2><%= html_encode(post.subject) %></h2> <% end ``` - Backticks: `` `stats` `` runs the statements and returns everything they wrote to `Io.stdout`, including `Io.print`, as a string. Backticks aren't statements. ## Modules - A chunk starts with only one local, `require`. Everything else is imported, at the top of the file: ```lua local Luan = require "luan:Luan.luan" local error = Luan.error local ipairs = Luan.ipairs or error() local String = require "luan:String.luan" local starts_with = String.starts_with or error() ``` - `require` takes a URI: `luan:X.luan` (the standard library, `src/luan/modules/X.luan`), `site:/X` (the site's `src/` directory), `file:`, `classpath:` and others. `Package.load(uri)` is the same but returns `false` for a missing module instead of throwing. - A module returns a table of functions, built up and returned at the end: ```lua local Utils = {} function Utils.check_referer() ... end return Utils ``` - Modules in a site's `lib/` that use each other require each other inside the functions that use them, not at the top of the file, so that adding a circular dependency later never forces a change: ```lua function post.get_chat() local Chat = require "site:/lib/Chat.luan" return Chat.get_by_id(post.chat_id) end ``` ## Objects There are no classes. An object is a table, and a constructor adds closures to it: ```lua function Post.new(post) function post.save() ... end function post.delete() ... end return post end ``` Callers write `post.save()`, not `post:save()`. ## Threads Luan has real threads instead of coroutines. Each interpreter (`src/luan/Luan.java`) lives in one thread, and an object becomes immutable as soon as two interpreters can reach it. So application code never synchronizes. For values that must differ per thread, libraries keep per-interpreter values: `Luan.get_local_cloned`/`set_local_cloned` keep a value that is copied into interpreters cloned from this one, and `get_local_only`/`set_local_only` keep one that isn't (as for `Http.request` and `Http.response`). Prefer local cloned unless local only is clearly needed. Application code rarely needs either. ## Compilation Luan compiles each chunk to Java source, then to class files, which it loads. That's why it needs a JDK, not just a JRE. ## Calling Java Any Java method can be called from Luan with no glue code. This is how Luan's own library is built, and how a Luan application uses a Java library. - `require "java"` turns on Java in the chunk, if the chunk has permission. Hosted sites don't have permission, so on hosting only the standard library uses Java on a site's behalf. - `require "java:pkg.Class"` returns the class. Static methods, static fields and nested classes are its fields, and `.new(...)` calls a constructor. - Methods of a Java object are bound to it, so they can be stored and called as plain functions: `local append = sb.append`. - Arguments are converted to the parameter types. A boxed type like `Integer` accepts `nil`, which is how a Java method takes an optional argument. A first parameter `Luan luan` is supplied automatically. - A Java array comes back as multiple return values. - There is no getter or setter mapping: call `obj.getName()`. - Java keywords get a trailing underscore: `StringLuan.char_` is exported as `String.char`. - Overloaded methods are resolved at call time, but a library is clearer with one unambiguous Java method written for Luan to call. ### How the standard library is built A module `src/luan/modules/X.luan` builds its table by assigning functions from a Java class, usually `XLuan.java` next to it: `String.find = StringLuan.find`. To find out what a function does, go from the `.luan` file to the Java method. Java objects are usually wrapped in tables that expose selected bound methods, plus a `java` field holding the object. Lists are returned as Luan tables. ## Fail fast In a dynamic language, robustness comes from finding mistakes as close to their cause as possible. Use `or error()` freely: - On every import: `local f = Module.f or error()`. - On required values: `local id = Http.request.parameters.id or error()`. - On lookups that must succeed: `local post = Post.get_by_id(id) or error "post not found"`. - On conditions: `x > 0 or error "x must be positive"`.