Luam is a modernized fork of Lua 5.1, featuring a stricter, safer, and more concise syntax. It preserves the speed and simplicity of Lua 5.1 while selectively adopting features from later Lua versions and modern programming paradigms.
The local keyword has been removed. A bare assignment to a name that
isn't already a local or upvalue declares a new local in the current block
instead of writing a global — this is true for any loaded chunk (a file,
load, loadstring, dofile, require, ...). The one exception is the
interactive prompt (luam -e, the REPL), where bare assignment still writes
a real global so that variables persist across separate lines typed at the
prompt.
x = 10 -- local to this chunk/blockA bare function statement (function f() ... end) gets exactly the
same implicit-local treatment as f = function() ... end — both create a
genuine local, and the two forms are interchangeable for this rule despite
looking like two different things. Activation happens before the
function's own body is compiled, so a function can always call itself by
its own bare name and recurse correctly. Calling a different function
defined later in the same file is the one case that doesn't work for
free: that name doesn't exist yet at the point the earlier code compiles,
so the reference falls through to an unresolved global — nil at call
time, a loud crash at the exact call site, not silent misbehavior. See
doc/forward_references.md for the full rules
and the pre-declaration idiom for when a forward reference (including
genuine mutual recursion) is really needed. An earlier version of Luam
tried a different fix for the underlying collision bug this closes — see
doc/changelog.md for that history.
Because bare assignment and a bare function statement are both really
local, the common Lua idiom of loading a script and then reading back
whatever globals it set (lua_getglobal from C, or dofile(...); print(x) from Lua) no longer works by default for a plain variable or a
bare function — neither becomes visible outside the chunk that created it.
_G is still the same global table as in Lua 5.1, and reading an undeclared
name still resolves to a global. To deliberately create or modify a real
global from a script, assign through _G explicitly:
_G.config = { debug = true } -- visible to the host / other chunksgetfenv/setfenv are also still present, so a chunk's environment can be
swapped out the same way it can in Lua 5.1 (e.g. to sandbox untrusted code).
Use the const keyword to define immutable variables. Reassigning a const variable results in a compile-time error.
const y = 20
y = 30 -- Error: attempt to assign to const variableUse != instead of ~=.
if x != y then
-- ...
endTo simplify the language and enforce a single idiomatic style:
repeatuntillocal
have been removed.
All iterative logic is expressed using while loops.
if, while, and not require an actual boolean (true/false). There is
no truthy/falsy coercion, and a literal nil in a conditional is a
compile-time error:
if 0 then ... end -- error: conditional requires a boolean value
if nil then ... end -- error: nil is not a conditional value
if not nil then ... end -- error: 'not' requires a boolean value, got nil
value = get_value()
if value != nil then ... end -- OK: comparison produces a booleanWhen the compiler can already prove an expression is a boolean (e.g. the result of a comparison held in a local), the runtime check is skipped entirely — the check only costs anything for values it can't already prove safe. See doc/strict_not_operator.md and doc/error_handling.md for the full rules and migration patterns.
Multiline strings use """ instead of the traditional [[ ... ]] syntax.
s = """
Multi-line
String support
"""Strings support hexadecimal escapes using \xXX.
"A" == "\x41"Tables support the __len metamethod (backported from Lua 5.2), enabling custom length semantics.
Metatables and tag methods (setmetatable, getmetatable, __index,
__newindex, __call, ...) work the same as Lua 5.1's. This is a deliberate
tradeoff, not an oversight: Luam discourages OOP-style programming at the
syntax layer — colon method-call and method-definition sugar
(obj:method(), function obj:method() ... end) are removed, so any
metatable-based object still has to be called and defined the long way
(obj.method(obj, ...)). The underlying setmetatable/__index mechanism
itself stays fully enabled, because vendored libraries under lib/
(LuaSocket, the sqlite test framework, ...) are ordinary upstream Lua code
that relies on it — disabling it at the primitive level would mean rewriting
every vendored library rather than just declining to add ergonomic sugar for
new code.
xpcall accepts arguments passed directly to the called function (backported from Lua 5.2).
xpcall(func, handler, arg1, arg2)load(chunk) handles both functions and strings, replacing the need for loadstring.
math.log(x, base)supports an optional base argument.table.pack(...)creates a table from arguments and includes annfield.table.unpack(t)is standardized (renamed fromunpack).
os.exit(boolean)supportstruefor success andfalsefor failure.package.searchersis provided as an alias forpackage.loadersfor Lua 5.2 compatibility.
Luam provides a simplified build process via a dedicated shell script.
chmod +x bld/build_lang.sh
./bld/build_lang.shBuild artifacts are placed in the bin/ directory:
bin/luam— Interactive interpreterbin/luamc— Bytecode compilerbin/sqlite3.so— SQLite3 module
bld/build_lang.sh above builds Luam itself. A separate project written
in Luam that wants to ship as one standalone native executable (no
Luam installation required on the target machine) uses
lib/static/build.lua instead:
luam lib/static/build.lua --entry main.lua --bin myproject --with sqlite3,lfsIt flattens the project's own source together with Luam's stdlib and
any vendored modules it needs, compiles and preload-registers whatever
C extensions are requested (see MODULES in that file for the full
list), and links everything into one binary in ./bin/. Run it with
--help for the full option list. This replaces hand-rolling the same
temp-dir/flatten/compile/link logic in a project's own bld/build.sh
independently each time -- see daat's and brain-ex's own
bld/build.sh for the (now very thin) delegation this leaves behind.
Manual pages are provided in the doc/ directory:
doc/lua.1doc/luac.1
Design notes on specific language changes:
- doc/manifesto.md — the design tenets behind Luam, and how each one continues a goal Lua's own creators stated for Lua itself
- doc/error_handling.md — nil-check patterns under strict conditionals
- doc/forward_references.md — why calling a same-file function defined later doesn't work by default, and the zero-new-syntax pre-declaration idiom for when it's genuinely needed
- doc/strict_not_operator.md — the strict
notoperator and literal-nilrestrictions - doc/changelog.md — full list of differences from Lua 5.1, with measured (not estimated) size numbers
- doc/install.md — build targets and platform options
Luam is free software, released under the MIT License, matching Lua 5.1.