April 27, 2014 - Tagged as: haskell, en, lua, hslua.
TL;DR: It’s possible to call Haskell functions from Lua and Lua function from Haskell using hslua. “Callbacks” example in hslua repository shows how to do this.
UPDATE: Also see the follow-up post here.
hslua
provides Lua 5.1 C API to the programmer, and using it we can call Lua functions from Haskell and Haskell functions from Lua. In this post, I’m going to give two example Lua function implementations in Haskell. This functions will be mapped to a global name in Lua and one of them will be getting Lua functions as it’s arguments.
There are two ways of writing Lua functions in Haskell using hslua. First is the high-level method, where the Haskell function is just any function, provided that it’s type is an instance of LuaImport
(we’ll come to this later). Second method is what I’d like to call the raw Haskell function method. Raw Haskell functions should have the type LuaState -> IO CInt
, where LuaState
is Lua interpreter state provided by hslua
and return type CInt
represents the amount of return values(e.g. values that are left on the stack by this function). In raw functions, you need to do Lua stack manipulation using standard Lua 5.1 API.
While first method gives you a nice, abstracted way of writing Lua functions in Haskell, raw function method gives you the maximum amount of flexibility that Lua can provide. First method has lots of limitations when compared with raw method.
In first method all you have to do is to write a Haskell function with it’s type is an instance of LuaImport
. So let’s see what types are instances:
instance (StackValue a) => LuaImport (IO a) where
instance (StackValue a, LuaImport b) => LuaImport (a -> b)
So basically every function type where it’s argument types are instances of StackValue
and return type is IO a
where a
is also an instance of StackValue
is an instance of LuaImport
and thus can be used in this higher-level method of writing Lua functions in Haskell. Let’s see which types are StackValue
s:
instance StackValue LuaInteger
instance StackValue LuaNumber
instance StackValue Int
instance StackValue Double
instance StackValue String
instance StackValue Bool
instance StackValue (FunPtr LuaCFunction)
instance StackValue (Ptr a)
instance StackValue LuaState
instance StackValue ()
We have basic Haskell types Int
, Double
, String
, and Bool
as instances. Other types are for more advanced use, for example, Ptr a
is used for userdata
(basically any binary data that you want to pass to Lua stack and later get back, see reference manual for more details).
This two functions are instances of LuaImport
:
concat' :: String -> String -> IO String
= return $ s1 ++ s2
concat' s1 s2
pow :: Double -> Double -> IO Double
= return $ d1 ** d2 pow d1 d2
And we can push this functions to Lua stack using pushhsfunction
or directly assign them to a global variable using registerhsfunction
. In this post I’ll use register functions:
import Scripting.Lua as Lua
main :: IO ()
= do
main <- newstate
l
openlibs l"concat" concat'
registerhsfunction l "pow" pow
registerhsfunction l "haskellfun.lua"
loadfile l 0 0
call l close l
With 11 lines of code, we can create a Lua state and map this Haskell functions to some names and call them from Lua. This program runs Lua file haskellfun.lua
, which you can see in examples folder of hslua
repository.
Apart from the simplicity, another good thing about this method is that it handles type checking of Lua values automatically. Internally, this functions are wrapped with another function which gets LuaState
as parameter and collects Lua values from stack, checks their types(and throws error in case of a type mismatch), and push return value of the function to the Lua stack again. Here’s an example call with wrong type of values:
print(pow("wrong"))
...
bad argument #1 to '?' (number expected, got string)
Major limitation of this method is that you can only get basic Lua types from the Lua stack. For example, you can’t get a Lua table automatically like you get a Lua string. This because Lua needs to keep track of tables and some other values for garbage collection.
The Lua way of using Lua tables, Lua functions etc. in Lua API is to register that values to the Lua table called registry, and refer to that values using their index at registry. Using registry, Lua keeps tracks of references to Lua values that are available for garbage collection. See reference manual section 3.5 for more details.
We can do this writing raw Haskell functions. Raw functions have type LuaState -> IO CInt
and LuaState
allows us to run any C API function. raw functions are pushed to Lua stack using pushrawhsfunction
and registered as global variable using registerrawhsfunction
.
callbacks
example in the hslua
repository takes Lua callbacks in Haskell functions and later call them in FIFO order and return their return values as a Lua array(table with int keys). You can see the complete program in the repository and here I’ll give only the tricky parts.
Raw Haskell functions should return number of values left on the Lua stack as return values. As an example, addLuaCallbacks
function uses this for simple error reporting, it puts the error string to the Lua stack and return 1
in case of an error, and return 0
otherwise:
= do
addLuaCallbacks l ...
case as of
Nothing -> do
-- arguments are functions, add them to callback queue and return
-- nothing
1 args
addCallbacks return 0
Just errArg -> do
-- error: argument at `errArg` is not a function, return error
-- string
$ "argument " ++ show errArg ++ " is not a function"
pushstring l return 1
This example program keeps track of passed Lua callbacks in an IORef
. Here’s the part that handles getting Lua callbacks from Lua stack:
max
addCallbacks n | n > max = return ()
| otherwise = do
-- move nth argument to top of the stack
pushvalue l n-- add function reference to registry
<- ref l registryindex
refId -- add registry index to IORef
++ [refId])
modifyIORef cs (-- continue adding other arguments
+1) max addCallbacks (n
Note how we’re adding the function to the registry and getting it’s index at the registry in Haskell. We can now refer to this functions(e.g. push this function to Lua stack) using this index. Here’s the relevant code:
-- | Call Lua callbacks collected with `addLuaCallbacks`.
callLuaCallbacks :: LuaState -> IO CInt
= do
callLuaCallbacks l ...
where
= return ()
iter [] : rest) = do
iter (c ...
fromIntegral c)
pushinteger l (
gettable l registryindex-- call the callback
0 1
call l ...
iter rest
We’re pushing the index to the Lua stack, and calling gettable
to push actual function to the stack using the index. Complete program is longish, so I’m omitting it here, you can see it in hslua
s Github repository with an example Lua program that uses defined Haskell functions to pass Lua callbacks to Haskell.
Lua tolerates some incorrect stack operations and if you do that while writing raw Haskell functions, you can have hard times debugging your programs. Fortunately, Lua also provides a compile time flag to enable checking API usage for safety. If you install hslua
using -fapicheck
Cabal flag, it compiled Lua with API checking enabled and Lua gives you errors instead of silently doing something not intended. For example, if you refer to a Lua value at stack index -4
while your stack has only 3
elements, you get something like:
callbacks: src/lapi.c:57: index2adr: Assertion `idx != 0 && -idx <= L->top - L->base' failed.
This helps making sure that your API usage is correct.
hslua also provides a module for reading configuration files. As an example, using Scripting.Lua.ConfigFile.getNestedAssocLists
, you can execute the Lua file given below and get resulting nested table as Haskell [(String, [(String, String)])]
:
= {
someVal = {
something = "aaa",
foo = "bbb",
bar = "ccc"
baz },
= {
somethingElse ...
}
}
In all hslua examples, we needed to create Lua state in Haskell, register Haskell functions inside Haskell and then run Lua files using Lua C API again in Haskell. I think in theory it is also possible to run Lua programs using standard Lua interpreter executables(instead of running the interpreter using Lua C API inside Haskell) and load Haskell functions compiled to *.so
shared library files.
About one year ago, I did something similar using C instead of Haskell. You can see the code here. The library compiled to a .so
file and by using Lua’s package.loadlib
, you can load functions defined in the .so
.
Compiling raw Haskell hslua
functions to a .so
should not be tricky. LuaState
type is just a wrapper around Ptr ()
, and CInt
is just a C integer, so it’s signature is already compatible. We probably need to use foreign export ...
of GHC FFI and find GHC parameters to compile to .so
. I’ll investigate this further and post updates.