Luax
provides a safe, higher-level interface over Lua. Where the
Lua module returns raw values and status codes, Luax returns Maybe and
Result types, manages stack cleanup internally, and provides macros that
reduce boilerplate for common patterns.
Safe stack access. The maybe-get-* family type-checks before reading and
returns Nothing on mismatch, so you never get garbage from reading the wrong
type:
(Lua.push-int lua 42)
(match (Luax.maybe-get-int lua -1)
(Maybe.Just n) (IO.println &(fmt "got %d" n))
(Maybe.Nothing) (IO.errorln "not a number"))
(match (Luax.maybe-get-string lua -1)
(Maybe.Just _) () ; won't match — it's an int
(Maybe.Nothing) (IO.println "correctly rejected"))
(Lua.pop lua 1)
Globals. set-*-global and get-*-global handle the push/pop cycle for
you. The getters return Maybe and clean up the stack regardless of success:
(Luax.set-int-global lua "score" 100)
(Luax.set-bool-global lua "debug" true)
(match (Luax.get-int-global lua "score")
(Maybe.Just n) (IO.println &(fmt "score: %d" n))
(Maybe.Nothing) (IO.errorln "missing"))
Table fields. get-*-field reads a field from a table already on the stack
and pops the field value afterward, so you can read multiple fields in sequence
without manual stack management. set-*-field handles the index shift from
pushing the value:
(Lua.get-global lua (cstr "player"))
(match (Luax.get-string-field lua -1 "name")
(Maybe.Just name) (IO.println &name)
(Maybe.Nothing) (IO.errorln "no name"))
(match (Luax.get-int-field lua -1 "hp")
(Maybe.Just hp) (IO.println &(fmt "hp: %d" hp))
(Maybe.Nothing) (IO.errorln "no hp"))
(Luax.set-int-field lua -1 "hp" 0)
(Lua.pop lua 1)
Calling functions. call-fn looks up a global function, pushes
arguments, calls it, and reads the result — returning (Error msg) if the
function is undefined or raises a Lua error:
(match (Luax.call-fn lua add Lua.get-int
(Lua.push-int 3) (Lua.push-int 4))
(Result.Success v) (IO.println &(fmt "3 + 4 = %d" v))
(Result.Error e) (IO.errorln &e))
Building tables. make-table creates a table, sets fields,
and assigns it to a global in one expression:
(Luax.make-table lua point
(x (Lua.push-int 10))
(y (Lua.push-int 20)))
; point is now a Lua global with fields x=10, y=20
Code execution. do-in wraps Lua.do-string with
Result error handling:
(match (Luax.do-in lua "x = 1 + nil")
(Result.Success _) ()
(Result.Error e) (IO.println &(fmt "caught: %s" &e)))
call-fn
Macro
(call-fn lua name getter :rest push-exprs)
Call the Lua function name and read the result with getter.
Arguments are passed as push expressions—the lua state argument is inserted
automatically. Returns (Error msg) if the function is undefined or if the
call raises a Lua error.
(Luax.call-fn lua add Lua.get-int
(Lua.push-int 3)
(Lua.push-int 4))
do-in
(Fn [(Ref Lua a), (Ref String b)] (Result String String))
(do-in lua code)
Compile and execute the Lua string code. Returns (Success "")
on success or (Error msg) with the Lua error message on failure. Wraps
Lua.do-string with Result handling.
get-bool-field
(Fn [(Ref Lua a), Int, (Ref String b)] (Maybe Bool))
(get-bool-field lua index name)
Read field name from the table at index as a boolean.
Returns Nothing if the field is nil or not a boolean. Pops the field value
from the stack internally, leaving only the table.
get-bool-global
(Fn [(Ref Lua a), (Ref String b)] (Maybe Bool))
(get-bool-global lua name)
Fetch the global name as a boolean. Returns Nothing
if the global is nil or not a boolean. Pops the global from the stack internally.
get-carp-str
(Fn [(Ref Lua a), Int] String)
(get-carp-str lua index)
Read the value at index as a Carp String, using Lua's
tostring coercion. Returns an empty string if the conversion fails.
get-float-field
(Fn [(Ref Lua a), Int, (Ref String b)] (Maybe Float))
(get-float-field lua index name)
Read field name from the table at index as a float.
Returns Nothing if the field is nil or not a number. Pops the field value
from the stack internally, leaving only the table.
get-float-global
(Fn [(Ref Lua a), (Ref String b)] (Maybe Float))
(get-float-global lua name)
Fetch the global name as a float. Returns Nothing
if the global is nil or not a number. Pops the global from the stack internally.
get-int-field
(Fn [(Ref Lua a), Int, (Ref String b)] (Maybe Int))
(get-int-field lua index name)
Read field name from the table at index as an integer.
Returns Nothing if the field is nil or not a number. Pops the field value
from the stack internally, leaving only the table.
get-int-global
(Fn [(Ref Lua a), (Ref String b)] (Maybe Int))
(get-int-global lua name)
Fetch the global name as an integer. Returns Nothing
if the global is nil or not a number. Pops the global from the stack internally.
get-string-field
(Fn [(Ref Lua a), Int, (Ref String b)] (Maybe String))
(get-string-field lua index name)
Read field name from the table at index as a Carp
String. Returns Nothing if the field is nil or not a string. Pops the field
value from the stack internally, leaving only the table.
get-string-global
(Fn [(Ref Lua a), (Ref String b)] (Maybe String))
(get-string-global lua name)
Fetch the global name as a Carp String. Returns
Nothing if the global is nil or not a string. Pops the global from the stack
internally.
make-table
Macro
(make-table lua name :rest field-specs)
Create a Lua table with the given fields and assign it to the
global name. Each field spec is (field-name (push-expr)), where the lua
state argument is inserted into the push expression automatically.
(Luax.make-table lua player
(name (Lua.push-carp-str "Ada"))
(hp (Lua.push-int 100)))
maybe-get-bool
(Fn [(Ref Lua a), Int] (Maybe Bool))
(maybe-get-bool lua index)
Read the value at index as a boolean, returning
Nothing if it is not a boolean. Leaves the stack unchanged.
maybe-get-float
(Fn [(Ref Lua a), Int] (Maybe Float))
(maybe-get-float lua index)
Read the value at index as a float, returning
Nothing if it is not a number. Leaves the stack unchanged.
maybe-get-int
(Fn [(Ref Lua a), Int] (Maybe Int))
(maybe-get-int lua index)
Read the value at index as an integer, returning
Nothing if it is not a number. Leaves the stack unchanged.
maybe-get-string
(Fn [(Ref Lua a), Int] (Maybe String))
(maybe-get-string lua index)
Read the value at index as a Carp String, returning
Nothing if it is not a string. Leaves the stack unchanged.
set-bool-field
(Fn [(Ref Lua a), Int, (Ref String b), Bool] ())
(set-bool-field lua index name value)
Set field name to value on the table at index.
Handles the stack index shift from pushing the value internally.
set-bool-global
(Fn [(Ref Lua a), (Ref String b), Bool] ())
(set-bool-global lua name value)
Push value and assign it to the global name.
set-float-field
(Fn [(Ref Lua a), Int, (Ref String b), Float] ())
(set-float-field lua index name value)
Set field name to value on the table at index.
Handles the stack index shift from pushing the value internally.
set-float-global
(Fn [(Ref Lua a), (Ref String b), Float] ())
(set-float-global lua name value)
Push value and assign it to the global name.
set-int-field
(Fn [(Ref Lua a), Int, (Ref String b), Int] ())
(set-int-field lua index name value)
Set field name to value on the table at index.
Handles the stack index shift from pushing the value internally.
set-int-global
(Fn [(Ref Lua a), (Ref String b), Int] ())
(set-int-global lua name value)
Push value and assign it to the global name.
set-string-field
(Fn [(Ref Lua a), Int, (Ref String b), (Ref String c)] ())
(set-string-field lua index name value)
Set field name to the string value on the table at
index. Handles the stack index shift from pushing the value internally.