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

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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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

defn

(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.