diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,65 @@
 ## Changelog
+### 0.7.0
 
+- Tuples from pairs to octuples have been made instances of `FromLuaStack` and
+  `ToLuaStack`.
+- New functions `dostring` and `dofile` are provided to load and run strings and
+  files in a single step.
+- `LuaStatus` was renamed to `Status`, the *Lua* prefix was removed from its
+  type constructors.
+- The constructor `ErrFile` was added to `Status`. It is returned by `loadfile`
+  if the file cannot be read.
+- Remove unused FFI bindings and unused types, including all functions unsafe to
+  use from within Haskell and the library functions added with 0.5.0. Users with
+  special requirements should define their own wrappers and raw bindings.
+- The module *Foreign.Lua.Api.SafeBindings* was merge into
+  *Foreign.Lua.Api.RawBindings*.
+- FFI bindings are changed to use newtypes where sensible, most notably
+  `StackIndex`, `NumArgs`, and `NumResults`, but also the newly introduced
+  newtypes `StatusCode`, `TypeCode`, and `LuaBool`.
+- Add functions `tointegerx` and `tonumberx` which can be used to get and check
+  values from the stack in a single step.
+- The signature of `concat` was changed from `Int -> Lua ()` to
+  `NumArgs -> Lua ()`.
+- The signature of `loadfile` was changed from `String -> Lua Int` to
+  `String -> Lua Status`. 
+- The type `LTYPE` was renamed to `Type`, its constructors were renamed to
+  follow the pattern `Type<Typename>`. `LuaRelation` was renamed to
+  `RelationalOperator`, the *Lua* prefix was removed from its constructors.
+- Add function `tolist` to allow getting a generic list from the stack without
+  having to worry about the overlapping instance with `[Char]`.
+
+
+### 0.6.0
+
+* Supported Lua Versions now include Lua 5.2 and Lua 5.3. LuaJIT and Lua 5.1
+  remain supported as well.
+* Flag `use-pkgconfig` was added to allow discovery of library and include paths
+  via pkg-config. Setting a specific Lua version flag now implies `system-lua`.
+  (Sean Proctor)
+* The module was renamed from `Scripting.Lua` to `Foreign.Lua`. The code is now
+  split over multiple sub-modules. Files processed with hsc2hs are restricted to
+  Foreign.Lua.Api.
+* A `Lua` monad (reader monad over LuaState) is introduced. Functions which took
+  a LuaState as their first argument are changed into monadic functions within
+  that monad.
+* Error handling has been redesigned completely. A new LuaException was
+  introduced and is thrown in unexpected situations. Errors in lua which are
+  leading to a `longjmp` are now caught with the help of additional C wrapper
+  functions. Those no longer lead to uncontrolled program termination but are
+  converted into a LuaException.
+* `peek` no longer returns `Maybe a` but just `a`. A LuaException is thrown if
+  an error occurs (i.e. in situtations where Nothing would have been returned
+  previously).
+* The `StackValue` typeclass has been split into `FromLuaStack` and
+  `ToLuaStack`. Instances not satisfying the law `x == push x *> peek (-1)` have
+  been dropped.
+* Documentation of API functions was improved. Most docstrings have been copied
+  from the official Lua manual, enriched with proper markup and links, and
+  changed to properly describe hslua specifics when necessary.
+* Example programs have been moved to a separate repository.
+* Unused files were removed. (Sean Proctor)
+
 ### 0.5.0
 
 * New raw functions for `luaopen_base`, `luaopen_package`, `luaopen_string`,
@@ -10,7 +70,7 @@
 * Drop support for GHC versions < 7.8, avoid compiler warnings.
 * Ensure no symbols are stripped when linking the bundled lua interpreter.
 * Simplify `tostring` function definition. (Sean Proctor)
-* Explicitly decprecate `strlen`. (Sean Proctor)
+* Explicitly deprecate `strlen`. (Sean Proctor)
 * Add links to lua documentation for functions wrapping the official lua C API.
   (Sean Proctor).
 
diff --git a/hslua.cabal b/hslua.cabal
--- a/hslua.cabal
+++ b/hslua.cabal
@@ -1,5 +1,5 @@
 name:                   hslua
-version:                0.6.0
+version:                0.7.0
 stability:              beta
 cabal-version:          >= 1.8
 license:                MIT
@@ -19,7 +19,7 @@
                         5.3.4. If you want to link it with a system-wide Lua
                         installation, use the @system-lua@ flag.
                         .
-                        <https://github.com/tarleb/hslua-examples Example programs>
+                        <https://github.com/hslua/hslua-examples Example programs>
                         are available in a separate repository.
 category:               Foreign
 extra-source-files:     lua-5.3.4/*.h
@@ -27,6 +27,9 @@
                         README.md
                         CHANGELOG.md
                         COPYRIGHT
+                        test/lua/syntax-error.lua
+                        test/lua/error.lua
+                        test/lua/example.lua
 
 source-repository head
   type:                 git
@@ -69,7 +72,6 @@
                       , Foreign.Lua.Api.Constants
                       , Foreign.Lua.Api.RawBindings
                       , Foreign.Lua.Api.Types
-                      , Foreign.Lua.Api.SafeBindings
                       , Foreign.Lua.FunctionCalling
                       , Foreign.Lua.Types
                       , Foreign.Lua.Types.Error
diff --git a/src/Foreign/Lua.hs b/src/Foreign/Lua.hs
--- a/src/Foreign/Lua.hs
+++ b/src/Foreign/Lua.hs
@@ -42,9 +42,11 @@
   -- * Receiving values from Lua stack (Lua → Haskell)
   , FromLuaStack (..)
   , peekEither
+  , toList
   , pairsFromTable
   -- * Pushing values to Lua stack (Haskell → Lua)
   , ToLuaStack (..)
+  , pushList
   -- * Calling Functions
   , PreCFunction
   , HaskellFunction
diff --git a/src/Foreign/Lua/Api.hs b/src/Foreign/Lua/Api.hs
--- a/src/Foreign/Lua/Api.hs
+++ b/src/Foreign/Lua/Api.hs
@@ -67,9 +67,10 @@
   , replace
   , checkstack
   -- ** types and type checks
-  , LTYPE (..)
-  , fromLuaType
-  , toLuaType
+  , Type (..)
+  , TypeCode (..)
+  , fromType
+  , toType
   , ltype
   , typename
   , isboolean
@@ -88,7 +89,9 @@
   , toboolean
   , tocfunction
   , tointeger
+  , tointegerx
   , tonumber
+  , tonumberx
   , topointer
   , tostring
   , tothread
@@ -97,8 +100,8 @@
   , rawlen
   , strlen
   -- ** Comparison and arithmetic functions
-  , LuaRelation (..)
-  , fromLuaRelation
+  , RelationalOperator (..)
+  , fromRelationalOperator
   , compare
   , equal
   , lessthan
@@ -136,8 +139,8 @@
   , loadfile
   , loadstring
   -- ** Coroutine functions
-  , LuaStatus (..)
-  , toLuaStatus
+  , Status (..)
+  , toStatus
   , status
   -- ** garbage-collection function and options
   , GCCONTROL (..)
@@ -158,22 +161,24 @@
   , openstring
   , opentable
   -- * Auxiliary library
+  , dostring
+  , dofile
   , newmetatable
   , ref
   , unref
   ) where
 
-import Prelude hiding (compare, concat)
+import Prelude hiding (EQ, LT, compare, concat)
 
 import Control.Monad
+import Data.Maybe (fromMaybe)
 import Foreign.C
 import Foreign.Lua.Api.Constants
 import Foreign.Lua.Api.RawBindings
-import Foreign.Lua.Api.SafeBindings
 import Foreign.Lua.Api.Types
 import Foreign.Lua.Types.Error
 import Foreign.Lua.Types.Lua
-import Foreign.Marshal.Alloc
+import Foreign.Marshal.Alloc (alloca)
 import Foreign.Ptr
 
 import qualified Data.ByteString as B
@@ -186,8 +191,7 @@
 --
 
 -- | Throw a lua error if the computation signaled a failure.
--- | Throw a lua error if the computation signaled a failure.
-throwOnError :: CInt -> Lua CInt
+throwOnError :: Failable a -> Lua CInt
 throwOnError x =
   if x < 0
   then throwTopMessageAsError
@@ -242,7 +246,7 @@
 call :: NumArgs -> NumResults -> Lua ()
 call nargs nresults = do
   res <- pcall nargs nresults Nothing
-  when (res /= LuaOK) throwTopMessageAsError
+  when (res /= OK) throwTopMessageAsError
 
 -- | Ensures that the stack has space for at least @n@ extra slots (that is,
 -- that you can safely push up to @n@ values into it). It returns false if it
@@ -255,7 +259,7 @@
 -- This is a wrapper function of
 -- <https://www.lua.org/manual/5.3/manual.html#lua_checkstack lua_checkstack>.
 checkstack :: Int -> Lua Bool
-checkstack n = liftLua $ \l -> (/= 0) <$> lua_checkstack l (fromIntegral n)
+checkstack n = liftLua $ \l -> fromLuaBool <$> lua_checkstack l (fromIntegral n)
 
 -- | Destroys all objects in the given Lua state (calling the corresponding
 -- garbage-collection metamethods, if any) and frees all dynamic memory used by
@@ -284,22 +288,18 @@
 --
 -- This is a wrapper function of
 -- <https://www.lua.org/manual/5.3/manual.html#lua_compare lua_compare>.
-compare :: StackIndex -> StackIndex -> LuaRelation -> Lua Bool
+compare :: StackIndex -> StackIndex -> RelationalOperator -> Lua Bool
 #if LUA_VERSION_NUMBER >= 502
 compare idx1 idx2 relOp = fmap (/= 0) . throwOnError =<< do
-  liftLua $ \l ->
-    hslua_compare l
-      (fromStackIndex idx1)
-      (fromStackIndex idx2)
-      (fromLuaRelation relOp)
+  liftLua $ \l -> hslua_compare l idx1 idx2 (fromRelationalOperator relOp)
 #else
 compare idx1 idx2 op = liftLua $ \l ->
   (/= 0) <$>
   case op of
-    LuaEQ -> lua_equal l (fromStackIndex idx1) (fromStackIndex idx2)
-    LuaLT -> lua_lessthan l (fromStackIndex idx1) (fromStackIndex idx2)
-    LuaLE -> (+) <$> lua_equal l (fromStackIndex idx1) (fromStackIndex idx2)
-                 <*> lua_lessthan l (fromStackIndex idx1) (fromStackIndex idx2)
+    EQ -> lua_equal l idx1 idx2
+    LT -> lua_lessthan l idx1 idx2
+    LE -> (+) <$> lua_equal l idx1 idx2
+              <*> lua_lessthan l idx1 idx2
 #endif
 
 -- | Concatenates the @n@ values at the top of the stack, pops them, and leaves
@@ -311,8 +311,8 @@
 --
 -- This is a wrapper function of
 -- <https://www.lua.org/manual/5.3/manual.html#lua_concat lua_concat>.
-concat :: Int -> Lua ()
-concat n = void . throwOnError =<< liftLua (`hslua_concat` fromIntegral n)
+concat :: NumArgs -> Lua ()
+concat n = void . throwOnError =<< liftLua (`hslua_concat` n)
 
 -- | Copies the element at index @fromidx@ into the valid index @toidx@,
 -- replacing the value at that position. Values at other positions are not
@@ -322,8 +322,7 @@
 -- the lua manual.
 copy :: StackIndex -> StackIndex -> Lua ()
 #if LUA_VERSION_NUMBER >= 503
-copy fromidx toidx = liftLua $ \l ->
-  lua_copy l (fromStackIndex fromidx) (fromStackIndex toidx)
+copy fromidx toidx = liftLua $ \l -> lua_copy l fromidx toidx
 #else
 copy fromidx toidx = do
   pushvalue fromidx
@@ -344,6 +343,25 @@
 createtable narr nrec = liftLua $ \l ->
   lua_createtable l (fromIntegral narr) (fromIntegral nrec)
 
+-- | Loads and runs the given string.
+--
+-- Returns @'OK'@ on success, or an error if either loading of the string or
+-- calling of the thunk failed.
+dostring :: String -> Lua Status
+dostring s = do
+  loadRes <- loadstring s
+  if loadRes == OK
+    then pcall 0 multret Nothing
+    else return loadRes
+
+-- | Loads and runs the given file.
+dofile :: FilePath -> Lua Status
+dofile fp = do
+  loadRes <- loadfile fp
+  if loadRes == OK
+    then pcall 0 multret Nothing
+    else return loadRes
+
 -- TODO: implement dump
 
 -- | Returns @True@ if the two values in acceptable indices index1 and index2
@@ -351,7 +369,7 @@
 -- call metamethods). Otherwise returns False. Also returns False if any of the
 -- indices is non valid. Uses @'compare'@ internally.
 equal :: StackIndex -> StackIndex -> Lua Bool
-equal index1 index2 = compare index1 index2 LuaEQ
+equal index1 index2 = compare index1 index2 EQ
 
 -- |  Controls the garbage collector.
 --
@@ -397,7 +415,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_getfield lua_getfield>.
 getfield :: StackIndex -> String -> Lua ()
 getfield i s = void . throwOnError =<< liftLua
-  (\l -> withCString s (hslua_getfield l (fromStackIndex i)))
+  (\l -> withCString s (hslua_getfield l i))
 
 -- | Pushes onto the stack the value of the global @name@. Returns the type of
 -- that value.
@@ -416,7 +434,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_getmetatable lua_getmetatable>.
 getmetatable :: StackIndex -> Lua Bool
 getmetatable n = liftLua $ \l ->
-  fmap (/= 0) (lua_getmetatable l (fromStackIndex n))
+  fmap (/= 0) (lua_getmetatable l n)
 
 -- | Pushes onto the stack the value @t[k]@, where @t@ is the value at the given
 -- index and @k@ is the value at the top of the stack.
@@ -430,7 +448,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_gettable lua_gettable>.
 gettable :: StackIndex -> Lua ()
 gettable n = void . throwOnError =<<
-  liftLua (\l -> hslua_gettable l (fromStackIndex n))
+  liftLua (\l -> hslua_gettable l n)
 
 -- | Returns the index of the top element in the stack. Because indices start at
 -- 1, this result is equal to the number of elements in the stack (and so 0
@@ -438,7 +456,7 @@
 --
 -- See also: <https://www.lua.org/manual/5.3/manual.html#lua_gettop lua_gettop>.
 gettop :: Lua StackIndex
-gettop = liftLua $ fmap fromIntegral . lua_gettop
+gettop = liftLua lua_gettop
 
 -- | Moves the top element into the given valid index, shifting up the elements
 -- above this index to open space. This function cannot be called with a
@@ -448,9 +466,9 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_insert lua_insert>.
 insert :: StackIndex -> Lua ()
 #if LUA_VERSION_NUMBER >= 503
-insert index = liftLua $ \l -> lua_rotate l (fromStackIndex index) 1
+insert index = liftLua $ \l -> lua_rotate l index 1
 #else
-insert index = liftLua $ \l -> lua_insert l (fromStackIndex index)
+insert index = liftLua $ \l -> lua_insert l index
 #endif
 
 -- | Returns @True@ if the value at the given index is a boolean, and @False@
@@ -459,7 +477,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isboolean lua_isboolean>.
 isboolean :: StackIndex -> Lua Bool
-isboolean n = (== TBOOLEAN) <$> ltype n
+isboolean n = (== TypeBoolean) <$> ltype n
 
 -- | Returns @True@ if the value at the given index is a C function, and @False@
 -- otherwise.
@@ -467,7 +485,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_iscfunction lua_iscfunction>.
 iscfunction :: StackIndex -> Lua Bool
-iscfunction n = liftLua $ \l -> (/= 0) <$> lua_iscfunction l (fromStackIndex n)
+iscfunction n = liftLua $ \l -> fromLuaBool <$> lua_iscfunction l n
 
 -- | Returns @True@ if the value at the given index is a function (either C or
 -- Lua), and @False@ otherwise.
@@ -475,7 +493,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isfunction lua_isfunction>.
 isfunction :: StackIndex -> Lua Bool
-isfunction n = (== TFUNCTION) <$> ltype n
+isfunction n = (== TypeFunction) <$> ltype n
 
 -- | Returns @True@ if the value at the given index is a light userdata, and
 -- @False@ otherwise.
@@ -484,7 +502,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_islightuserdata \
 -- lua_islightuserdata>.
 islightuserdata :: StackIndex -> Lua Bool
-islightuserdata n = (== TLIGHTUSERDATA) <$> ltype n
+islightuserdata n = (== TypeLightUserdata) <$> ltype n
 
 -- | Returns @True@ if the value at the given index is @nil@, and @False@
 -- otherwise.
@@ -492,14 +510,14 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isnil lua_isnil>.
 isnil :: StackIndex -> Lua Bool
-isnil n = (== TNIL) <$> ltype n
+isnil n = (== TypeNil) <$> ltype n
 
 -- | Returns @True@ if the given index is not valid, and @False@ otherwise.
 --
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isnone lua_isnone>.
 isnone :: StackIndex -> Lua Bool
-isnone n = (== TNONE) <$> ltype n
+isnone n = (== TypeNone) <$> ltype n
 
 -- | Returns @True@ if the given index is not valid or if the value at the given
 -- index is @nil@, and @False@ otherwise.
@@ -507,7 +525,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isnoneornil lua_isnoneornil>.
 isnoneornil :: StackIndex -> Lua Bool
-isnoneornil idx = (<= TNIL) <$> ltype idx
+isnoneornil idx = (<= TypeNil) <$> ltype idx
 
 -- | Returns @True@ if the value at the given index is a number or a string
 -- convertible to a number, and @False@ otherwise.
@@ -515,7 +533,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isnumber lua_isnumber>.
 isnumber :: StackIndex -> Lua Bool
-isnumber n = liftLua $ \l -> (/= 0) <$> lua_isnumber l (fromStackIndex n)
+isnumber n = liftLua $ \l -> fromLuaBool <$> lua_isnumber l n
 
 -- | Returns @True@ if the value at the given index is a string or a number
 -- (which is always convertible to a string), and @False@ otherwise.
@@ -523,7 +541,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isstring lua_isstring>.
 isstring :: StackIndex -> Lua Bool
-isstring n = liftLua $ \l -> (/= 0) <$> lua_isstring l (fromStackIndex n)
+isstring n = liftLua $ \l -> fromLuaBool <$> lua_isstring l n
 
 -- | Returns @True@ if the value at the given index is a table, and @False@
 -- otherwise.
@@ -531,7 +549,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_istable lua_istable>.
 istable :: StackIndex -> Lua Bool
-istable n = (== TTABLE) <$> ltype n
+istable n = (== TypeTable) <$> ltype n
 
 -- | Returns @True@ if the value at the given index is a thread, and @False@
 -- otherwise.
@@ -539,7 +557,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isthread lua_isthread>.
 isthread :: StackIndex -> Lua Bool
-isthread n = (== TTHREAD) <$> ltype n
+isthread n = (== TypeThread) <$> ltype n
 
 -- | Returns @True@ if the value at the given index is a userdata (either full
 -- or light), and @False@ otherwise.
@@ -547,7 +565,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_isuserdata lua_isuserdata>.
 isuserdata :: StackIndex -> Lua Bool
-isuserdata n = liftLua $ \l -> (/= 0) <$> lua_isuserdata l (fromStackIndex n)
+isuserdata n = liftLua $ \l -> fromLuaBool <$> lua_isuserdata l n
 
 -- | This is a convenience function to implement error propagation convention
 -- described in [Error handling in hslua](#g:1). hslua doesn't implement
@@ -562,30 +580,28 @@
 -- | Tests whether the object under the first index is smaller than that under
 -- the second. Uses @'compare'@ internally.
 lessthan :: StackIndex -> StackIndex -> Lua Bool
-lessthan index1 index2 = compare index1 index2 LuaLT
+lessthan index1 index2 = compare index1 index2 LT
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_loadfile luaL_loadfile>.
-loadfile :: String -> Lua Int
+loadfile :: String -> Lua Status
 #if LUA_VERSION_NUMBER >= 502
 loadfile f = liftLua $ \l ->
   withCString f $ \fPtr ->
-  fromIntegral <$> luaL_loadfilex l fPtr nullPtr
+  toStatus <$> luaL_loadfilex l fPtr nullPtr
 #else
 loadfile f = liftLua $ \l ->
   withCString f $ \fPtr ->
-  fromIntegral <$> luaL_loadfile l fPtr
+  toStatus <$> luaL_loadfile l fPtr
 #endif
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_loadstring luaL_loadstring>.
-loadstring :: String -> Lua LuaStatus
+loadstring :: String -> Lua Status
 loadstring str = liftLua $ \l ->
-  withCString str $ \strPtr ->
-  toLuaStatus <$> luaL_loadstring l strPtr
+  withCString str (fmap toStatus . luaL_loadstring l)
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_type lua_type>.
-ltype :: StackIndex -> Lua LTYPE
-ltype idx = toLuaType <$>
-  liftLua (flip lua_type $ fromStackIndex idx)
+ltype :: StackIndex -> Lua Type
+ltype idx = toType <$> liftLua (flip lua_type idx)
 
 -- | If the registry already has the key tname, returns @False@. Otherwise,
 -- creates a new table to be used as a metatable for userdata, adds to this new
@@ -600,7 +616,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#luaL_newmetatable luaL_newmetatable>.
 newmetatable :: String -> Lua Bool
 newmetatable tname = liftLua $ \l ->
-  (/= 0) <$> withCString tname (luaL_newmetatable l)
+  fromLuaBool <$> withCString tname (luaL_newmetatable l)
 
 -- | Creates a new Lua state. It calls @'lua_newstate'@ with an allocator based
 -- on the standard C @realloc@ function and then sets a panic function (see
@@ -643,7 +659,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_next lua_next>.
 next :: StackIndex -> Lua Bool
 next idx = fmap (/= 0) . throwOnError =<<
-  liftLua (\l -> hslua_next l (fromStackIndex idx))
+  liftLua (\l -> hslua_next l idx)
 
 {-# DEPRECATED objlen "Use rawlen instead." #-}
 -- | Obsolete alias for @'rawlen'@.
@@ -732,23 +748,13 @@
 -- after the return of @'pcall'@, since by then the stack has unwound.
 --
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_pcall lua_pcall>.
-pcall :: NumArgs -> NumResults -> Maybe StackIndex -> Lua LuaStatus
+pcall :: NumArgs -> NumResults -> Maybe StackIndex -> Lua Status
 #if LUA_VERSION_NUMBER >= 502
 pcall nargs nresults msgh = liftLua $ \l ->
-  toLuaStatus <$>
-  lua_pcallk l
-    (fromNumArgs nargs)
-    (fromNumResults nresults)
-    (maybe 0 fromStackIndex msgh)
-    0
-    nullPtr
+  toStatus <$> lua_pcallk l nargs nresults (fromMaybe 0 msgh) 0 nullPtr
 #else
 pcall nargs nresults msgh = liftLua $ \l ->
-  toLuaStatus <$>
-  lua_pcall l
-    (fromNumArgs nargs)
-    (fromNumResults nresults)
-    (maybe 0 fromStackIndex msgh)
+  toStatus <$> lua_pcall l nargs nresults (fromMaybe 0 msgh)
 #endif
 
 -- | Pops @n@ elements from the stack.
@@ -762,7 +768,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_pushboolean lua_pushboolean>.
 pushboolean :: Bool -> Lua ()
-pushboolean b = liftLua $ \l -> lua_pushboolean l (fromIntegral (fromEnum b))
+pushboolean b = liftLua $ \l -> lua_pushboolean l (toLuaBool b)
 
 -- | Pushes a new C closure onto the stack.
 --
@@ -849,7 +855,7 @@
 --
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_pushvalue lua_pushvalue>.
 pushvalue :: StackIndex -> Lua ()
-pushvalue n = liftLua $ \l -> lua_pushvalue l (fromStackIndex n)
+pushvalue n = liftLua $ \l -> lua_pushvalue l n
 
 -- | Returns @True@ if the two values in indices @idx1@ and @idx2@ are
 -- primitively equal (that is, without calling the @__eq@ metamethod). Otherwise
@@ -859,14 +865,14 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawequal lua_rawequal>.
 rawequal :: StackIndex -> StackIndex -> Lua Bool
 rawequal idx1 idx2 = liftLua $ \l ->
-  (/= 0) <$> lua_rawequal l (fromStackIndex idx1) (fromStackIndex idx2)
+  fromLuaBool <$> lua_rawequal l idx1 idx2
 
 -- | Similar to @'gettable'@, but does a raw access (i.e., without metamethods).
 --
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawget lua_rawget>.
 rawget :: StackIndex -> Lua ()
-rawget n = liftLua $ \l -> lua_rawget l (fromStackIndex n)
+rawget n = liftLua $ \l -> lua_rawget l n
 
 -- | Pushes onto the stack the value @t[n]@, where @t@ is the table at the given
 -- index. The access is raw, that is, it does not invoke the @__index@
@@ -875,7 +881,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawgeti lua_rawgeti>.
 rawgeti :: StackIndex -> Int -> Lua ()
-rawgeti k m = liftLua $ \l -> lua_rawgeti l (fromStackIndex k) (fromIntegral m)
+rawgeti k m = liftLua $ \l -> lua_rawgeti l k (fromIntegral m)
 
 -- | Returns the raw "length" of the value at the given index: for strings, this
 -- is the string length; for tables, this is the result of the length operator
@@ -886,9 +892,9 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawlen lua_rawlen>.
 rawlen :: StackIndex -> Lua Int
 #if LUA_VERSION_NUMBER >= 502
-rawlen idx = liftLua $ \l -> fromIntegral <$> lua_rawlen l (fromStackIndex idx)
+rawlen idx = liftLua $ \l -> fromIntegral <$> lua_rawlen l idx
 #else
-rawlen idx = liftLua $ \l -> fromIntegral <$> lua_objlen l (fromStackIndex idx)
+rawlen idx = liftLua $ \l -> fromIntegral <$> lua_objlen l idx
 #endif
 
 -- | Similar to @'settable'@, but does a raw assignment (i.e., without
@@ -897,7 +903,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawset lua_rawset>.
 rawset :: StackIndex -> Lua ()
-rawset n = liftLua $ \l -> lua_rawset l (fromStackIndex n)
+rawset n = liftLua $ \l -> lua_rawset l n
 
 -- | Does the equivalent of @t[i] = v@, where @t@ is the table at the given
 -- index and @v@ is the value at the top of the stack.
@@ -908,11 +914,11 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_rawseti lua_rawseti>.
 rawseti :: StackIndex -> Int -> Lua ()
-rawseti k m = liftLua $ \l -> lua_rawseti l (fromStackIndex k) (fromIntegral m)
+rawseti k m = liftLua $ \l -> lua_rawseti l k (fromIntegral m)
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_ref luaL_ref>.
 ref :: StackIndex -> Lua Int
-ref t = liftLua $ \l -> fromIntegral <$> luaL_ref l (fromStackIndex t)
+ref t = liftLua $ \l -> fromIntegral <$> luaL_ref l t
 
 -- | Sets the C function @f@ as the new value of global @name@.
 --
@@ -929,9 +935,9 @@
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_remove lua_remove>.
 remove :: StackIndex -> Lua ()
 #if LUA_VERSION_NUMBER >= 503
-remove n = liftLua (\l -> lua_rotate l (fromStackIndex n) (-1)) *> pop 1
+remove n = liftLua (\l -> lua_rotate l n (-1)) *> pop 1
 #else
-remove n = liftLua $ \l -> lua_remove l (fromStackIndex n)
+remove n = liftLua $ \l -> lua_remove l n
 #endif
 
 -- | Moves the top element into the given valid index without shifting any
@@ -941,9 +947,9 @@
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_replace lua_replace>.
 replace :: StackIndex -> Lua ()
 #if LUA_VERSION_NUMBER >= 503
-replace n = liftLua (\l -> lua_copy l (-1) (fromStackIndex n)) *> pop 1
+replace n = liftLua (\l -> lua_copy l (-1) n) *> pop 1
 #else
-replace n = liftLua $ \l ->  lua_replace l (fromStackIndex n)
+replace n = liftLua $ \l ->  lua_replace l n
 #endif
 
 -- | Does the equivalent to @t[k] = v@, where @t@ is the value at the given
@@ -958,7 +964,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_setfield lua_setfield>.
 setfield :: StackIndex -> String -> Lua ()
 setfield i s = void . throwOnError =<<
-  liftLua (\l -> withCString s (hslua_setfield l (fromStackIndex i)))
+  liftLua (\l -> withCString s (hslua_setfield l i))
 
 -- | Pops a value from the stack and sets it as the new value of global @name@.
 --
@@ -975,7 +981,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_setmetatable \
 -- lua_setmetatable>.
 setmetatable :: StackIndex -> Lua ()
-setmetatable idx = liftLua $ \l -> lua_setmetatable l (fromStackIndex idx)
+setmetatable idx = liftLua $ \l -> lua_setmetatable l idx
 
 -- | Does the equivalent to @t[k] = v@, where @t@ is the value at the given
 -- index, @v@ is the value at the top of the stack, and @k@ is the value just
@@ -990,7 +996,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_settable lua_settable>.
 settable :: StackIndex -> Lua ()
 settable index = void . throwOnError =<<
-  liftLua (\l -> hslua_settable l (fromStackIndex index))
+  liftLua (\l -> hslua_settable l index)
 
 -- | Accepts any index, or 0, and sets the stack top to this index. If the new
 -- top is larger than the old one, then the new elements are filled with nil. If
@@ -999,21 +1005,21 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_settop lua_settop>.
 settop :: StackIndex -> Lua ()
-settop = liftLua1 lua_settop . fromStackIndex
+settop = liftLua1 lua_settop
 
 -- |  Returns the status of this Lua thread.
 --
--- The status can be @'LuaOK'@ for a normal thread, an error value if the thread
--- finished the execution of a @'lua_resume'@ with an error, or @'LuaYield'@ if
+-- The status can be @'OK'@ for a normal thread, an error value if the thread
+-- finished the execution of a @'lua_resume'@ with an error, or @'Yield'@ if
 -- the thread is suspended.
 --
--- You can only call functions in threads with status @'LuaOK'@. You can resume
--- threads with status @'LuaOK'@ (to start a new coroutine) or @'LuaYield'@ (to
+-- You can only call functions in threads with status @'OK'@. You can resume
+-- threads with status @'OK'@ (to start a new coroutine) or @'Yield'@ (to
 -- resume a coroutine).
 --
 -- See also: <https://www.lua.org/manual/5.3/manual.html#lua_status lua_status>.
-status :: Lua LuaStatus
-status = liftLua $ fmap toLuaStatus . lua_status
+status :: Lua Status
+status = liftLua $ fmap toStatus . lua_status
 
 {-# DEPRECATED strlen "Use rawlen instead." #-}
 -- | Compatibility alias for rawlen
@@ -1028,7 +1034,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_toboolean lua_toboolean>.
 toboolean :: StackIndex -> Lua Bool
-toboolean n = liftLua $ \l -> (/= 0) <$> lua_toboolean l (fromStackIndex n)
+toboolean n = liftLua $ \l -> (/= 0) <$> lua_toboolean l n
 
 -- | Converts a value at the given index to a C function. That value must be a C
 -- function; otherwise, returns NULL.
@@ -1036,7 +1042,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_tocfunction lua_tocfunction>.
 tocfunction :: StackIndex -> Lua CFunction
-tocfunction n = liftLua $ \l -> lua_tocfunction l (fromStackIndex n)
+tocfunction n = liftLua $ \l -> lua_tocfunction l n
 
 -- | Converts the Lua value at the given acceptable index to the signed integral
 -- type @'lua_Integer'@. The Lua value must be an integer, a number or a string
@@ -1050,11 +1056,28 @@
 -- <https://www.lua.org/manual/5.3/manual.html#lua_tointeger lua_tointeger>.
 tointeger :: StackIndex -> Lua LuaInteger
 #if LUA_VERSION_NUMBER >= 502
-tointeger n = liftLua $ \l -> lua_tointegerx l (fromStackIndex n) 0
+tointeger n = liftLua $ \l -> lua_tointegerx l n nullPtr
 #else
-tointeger n = liftLua $ \l -> lua_tointeger l (fromStackIndex n)
+tointeger n = liftLua $ \l -> lua_tointeger l n
 #endif
 
+-- | Like @'tointeger'@, but returns @Nothing@ if the conversion failed
+tointegerx :: StackIndex -> Lua (Maybe LuaInteger)
+#if LUA_VERSION_NUMBER >= 502
+tointegerx n = liftLua $ \l -> alloca $ \bptr -> do
+  res <- lua_tointegerx l n bptr
+  isNum <- fromLuaBool <$> F.peek bptr
+  if isNum
+    then return $ Just res
+    else return $ Nothing
+#else
+tointegerx n = do
+  isNum <- isnumber n
+  if isNum
+    then Just <$> tointeger n
+    else return Nothing
+#endif
+
 -- | Converts the Lua value at the given index to the C type lua_Number. The Lua
 -- value must be a number or a string convertible to a number; otherwise,
 -- @tonumber@ returns 0.
@@ -1062,11 +1085,28 @@
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_tonumber lua_tonumber>.
 tonumber :: StackIndex -> Lua LuaNumber
 #if LUA_VERSION_NUMBER >= 502
-tonumber n = liftLua $ \l -> lua_tonumberx l (fromStackIndex n) 0
+tonumber n = liftLua $ \l -> lua_tonumberx l n nullPtr
 #else
-tonumber n = liftLua $ \l -> lua_tonumber l (fromStackIndex n)
+tonumber n = liftLua $ \l -> lua_tonumber l n
 #endif
 
+-- | Like @'tonumber'@, but returns @Nothing@ if the conversion failed
+tonumberx :: StackIndex -> Lua (Maybe LuaNumber)
+#if LUA_VERSION_NUMBER >= 502
+tonumberx n = liftLua $ \l -> alloca $ \bptr -> do
+  res <- lua_tonumberx l n bptr
+  isNum <- fromLuaBool <$> F.peek bptr
+  if isNum
+    then return $ Just res
+    else return $ Nothing
+#else
+tonumberx n = do
+  isNum <- isnumber n
+  if isNum
+    then Just <$> tonumber n
+    else return Nothing
+#endif
+
 -- | Converts the value at the given index to a generic C pointer (void*). The
 -- value can be a userdata, a table, a thread, or a function; otherwise,
 -- lua_topointer returns NULL. Different objects will give different pointers.
@@ -1077,14 +1117,14 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_topointer lua_topointer>.
 topointer :: StackIndex -> Lua (Ptr ())
-topointer n = liftLua $ \l -> lua_topointer l (fromStackIndex n)
+topointer n = liftLua $ \l -> lua_topointer l n
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tostring lua_tostring>.
 tostring :: StackIndex -> Lua B.ByteString
 tostring n = liftLua $ \l -> alloca $ \lenPtr -> do
-    cstr <- lua_tolstring l (fromStackIndex n) lenPtr
-    cstrLen <- F.peek lenPtr
-    B.packCStringLen (cstr, fromIntegral cstrLen)
+  cstr <- lua_tolstring l n lenPtr
+  cstrLen <- F.peek lenPtr
+  B.packCStringLen (cstr, fromIntegral cstrLen)
 
 -- | Converts the value at the given index to a Lua thread (represented as
 -- lua_State*). This value must be a thread; otherwise, the function returns
@@ -1093,7 +1133,7 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_tothread lua_tothread>.
 tothread :: StackIndex -> Lua LuaState
-tothread n = liftLua $ \l -> lua_tothread l (fromStackIndex n)
+tothread n = liftLua $ \l -> lua_tothread l n
 
 -- | If the value at the given index is a full userdata, returns its block
 -- address. If the value is a light userdata, returns its pointer. Otherwise,
@@ -1102,16 +1142,16 @@
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_touserdata lua_touserdata>.
 touserdata :: StackIndex -> Lua (Ptr a)
-touserdata n = liftLua $ \l -> lua_touserdata l (fromStackIndex n)
+touserdata n = liftLua $ \l -> lua_touserdata l n
 
 -- | Returns the name of the type encoded by the value @tp@, which must be one
 -- the values returned by @'ltype'@.
 --
 -- See also:
 -- <https://www.lua.org/manual/5.3/manual.html#lua_typename lua_typename>.
-typename :: LTYPE -> Lua String
+typename :: Type -> Lua String
 typename tp = liftLua $ \l ->
-  lua_typename l (fromLuaType tp) >>= peekCString
+  lua_typename l (fromType tp) >>= peekCString
 
 -- | Releases reference @'ref'@ from the table at index @idx@ (see @'ref'@). The
 -- entry is removed from the table, so that the referred object can be
@@ -1121,7 +1161,7 @@
 -- <https://www.lua.org/manual/5.3/manual.html#luaL_unref luaL_unref>.
 unref :: StackIndex -> Int -> Lua ()
 unref idx r = liftLua $ \l ->
-  luaL_unref l (fromStackIndex idx) (fromIntegral r)
+  luaL_unref l idx (fromIntegral r)
 
 -- | Returns the pseudo-index that represents the @i@-th upvalue of the running
 -- function (see <https://www.lua.org/manual/5.3/manual.html#4.4 §4.4> of the
diff --git a/src/Foreign/Lua/Api/RawBindings.hsc b/src/Foreign/Lua/Api/RawBindings.hsc
--- a/src/Foreign/Lua/Api/RawBindings.hsc
+++ b/src/Foreign/Lua/Api/RawBindings.hsc
@@ -42,6 +42,7 @@
 import Foreign.Ptr
 
 #include "lua.h"
+#include "safer-api.h"
 
 -- TODO: lua_getallocf, lua_setallocf
 -- TODO: Debugger functions
@@ -54,21 +55,13 @@
 --------------------------------------------------------------------------------
 -- * State manipulation
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_newstate lua_newstate>
-foreign import ccall unsafe "lua.h lua_newstate"
-  lua_newstate :: FunPtr LuaAlloc -> Ptr () -> IO LuaState
+-- lua_newstate is currently not supported.
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_close lua_close>
 foreign import ccall "lua.h lua_close"
   lua_close :: LuaState -> IO ()
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_newthread lua_newthread>
-foreign import ccall unsafe "lua.h lua_newthread"
-  lua_newthread :: LuaState -> IO LuaState
-
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_atpanic lua_atpanic>
-foreign import ccall "lua.h lua_atpanic"
-  lua_atpanic :: LuaState -> CFunction -> IO CFunction
+-- lua_newthread is currently not supported.
 
 
 --------------------------------------------------------------------------------
@@ -76,45 +69,43 @@
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_gettop lua_gettop>
 foreign import ccall unsafe "lua.h lua_gettop"
-  lua_gettop :: LuaState -> IO CInt
+  lua_gettop :: LuaState -> IO StackIndex
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_settop lua_settop>
 foreign import ccall unsafe "lua.h lua_settop"
-  lua_settop :: LuaState -> CInt -> IO ()
+  lua_settop :: LuaState -> StackIndex -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_pushvalue lua_pushvalue>
 foreign import ccall unsafe "lua.h lua_pushvalue"
-  lua_pushvalue :: LuaState -> CInt -> IO ()
+  lua_pushvalue :: LuaState -> StackIndex -> IO ()
 
 #if LUA_VERSION_NUMBER >= 503
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rotate lua_rotate>
 foreign import ccall unsafe "lua.h lua_rotate"
-  lua_rotate :: LuaState -> CInt -> CInt -> IO ()
+  lua_rotate :: LuaState -> StackIndex -> CInt -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_copy lua_copy>
 foreign import ccall unsafe "lua.h lua_copy"
-  lua_copy :: LuaState -> CInt -> CInt -> IO ()
+  lua_copy :: LuaState -> StackIndex -> StackIndex -> IO ()
 #else
 -- | See <https://www.lua.org/manual/5.2/manual.html#lua_remove lua_remove>
 foreign import ccall unsafe "lua.h lua_remove"
-  lua_remove :: LuaState -> CInt -> IO ()
+  lua_remove :: LuaState -> StackIndex -> IO ()
 
 -- | See <https://www.lua.org/manual/5.2/manual.html#lua_insert lua_insert>
 foreign import ccall unsafe "lua.h lua_insert"
-  lua_insert :: LuaState -> CInt -> IO ()
+  lua_insert :: LuaState -> StackIndex -> IO ()
 
 -- | See <https://www.lua.org/manual/5.2/manual.html#lua_replace lua_replace>
 foreign import ccall unsafe "lua.h lua_replace"
-  lua_replace :: LuaState -> CInt -> IO ()
+  lua_replace :: LuaState -> StackIndex -> IO ()
 #endif
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_checkstack lua_checkstack>
 foreign import ccall unsafe "lua.h lua_checkstack"
-  lua_checkstack :: LuaState -> CInt -> IO CInt
+  lua_checkstack :: LuaState -> StackIndex -> IO LuaBool
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_xmove lua_xmove>
-foreign import ccall unsafe "lua.h lua_xmove"
-  lua_xmove :: LuaState -> LuaState -> CInt -> IO ()
+-- lua_xmove is currently not supported.
 
 
 --------------------------------------------------------------------------------
@@ -122,90 +113,93 @@
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_isnumber lua_isnumber>
 foreign import ccall unsafe "lua.h lua_isnumber"
-  lua_isnumber :: LuaState -> CInt -> IO CInt
+  lua_isnumber :: LuaState -> StackIndex -> IO LuaBool
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_isstring lua_isstring>
 foreign import ccall unsafe "lua.h lua_isstring"
-  lua_isstring :: LuaState -> CInt -> IO CInt
+  lua_isstring :: LuaState -> StackIndex -> IO LuaBool
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_iscfunction lua_iscfunction>
 foreign import ccall unsafe "lua.h lua_iscfunction"
-  lua_iscfunction :: LuaState -> CInt -> IO CInt
+  lua_iscfunction :: LuaState -> StackIndex -> IO LuaBool
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_isuserdata lua_isuserdata>
 foreign import ccall unsafe "lua.h lua_isuserdata"
-  lua_isuserdata :: LuaState -> CInt -> IO CInt
+  lua_isuserdata :: LuaState -> StackIndex -> IO LuaBool
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_type lua_type>
 foreign import ccall unsafe "lua.h lua_type"
-  lua_type :: LuaState -> CInt -> IO CInt
+  lua_type :: LuaState -> StackIndex -> IO TypeCode
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_typename lua_typename>
 foreign import ccall unsafe "lua.h lua_typename"
-  lua_typename :: LuaState -> CInt -> IO (Ptr CChar)
+  lua_typename :: LuaState -> TypeCode -> IO (Ptr CChar)
 
-#if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_compare lua_compare>
-foreign import ccall "lua.h lua_compare"
-  lua_compare :: LuaState -> CInt -> CInt -> CInt -> IO CInt
+-- lua_compare is unsafe (might cause a longjmp), use hslua_compare instead.
+#if LUA_VERSION_NUM >= 502
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_compare \
+-- @lua_compare@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_compare"
+  hslua_compare :: LuaState -> StackIndex -> StackIndex -> CInt
+                -> IO (Failable LuaBool)
 #else
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_equal lua_equal>
 foreign import ccall "lua.h lua_equal"
-  lua_equal :: LuaState -> CInt -> CInt -> IO CInt
+  lua_equal :: LuaState -> StackIndex -> StackIndex -> IO CInt
 
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_lessthan lua_lessthan>
 foreign import ccall "lua.h lua_lessthan"
-  lua_lessthan :: LuaState -> CInt -> CInt -> IO CInt
+  lua_lessthan :: LuaState -> StackIndex -> StackIndex -> IO CInt
 #endif
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawequal lua_rawequal>
 foreign import ccall unsafe "lua.h lua_rawequal"
-  lua_rawequal :: LuaState -> CInt -> CInt -> IO CInt
+  lua_rawequal :: LuaState -> StackIndex -> StackIndex -> IO LuaBool
 
 --
 -- Type coercion
 --
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_toboolean lua_toboolean>
 foreign import ccall unsafe "lua.h lua_toboolean"
-  lua_toboolean :: LuaState -> CInt -> IO CInt
+  lua_toboolean :: LuaState -> StackIndex -> IO StackIndex
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tocfunction lua_tocfunction>
 foreign import ccall unsafe "lua.h lua_tocfunction"
-  lua_tocfunction :: LuaState -> CInt -> IO CFunction
+  lua_tocfunction :: LuaState -> StackIndex -> IO CFunction
 
 #if LUA_VERSION_NUMBER >= 502
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tointegerx lua_tointegerx>
 foreign import ccall unsafe "lua.h lua_tointegerx"
-  lua_tointegerx :: LuaState -> CInt -> CInt -> IO LuaInteger
+  lua_tointegerx :: LuaState -> StackIndex -> Ptr LuaBool -> IO LuaInteger
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tonumberx lua_tonumberx>
 foreign import ccall unsafe "lua.h lua_tonumberx"
-  lua_tonumberx :: LuaState -> CInt -> CInt -> IO LuaNumber
+  lua_tonumberx :: LuaState -> StackIndex -> Ptr LuaBool -> IO LuaNumber
 #else
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_tointeger lua_tointeger>
 foreign import ccall unsafe "lua.h lua_tointeger"
-  lua_tointeger :: LuaState -> CInt -> IO LuaInteger
+  lua_tointeger :: LuaState -> StackIndex -> IO LuaInteger
 
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_tonumber lua_tonumber>
 foreign import ccall unsafe "lua.h lua_tonumber"
-  lua_tonumber :: LuaState -> CInt -> IO LuaNumber
+  lua_tonumber :: LuaState -> StackIndex -> IO LuaNumber
 #endif
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tolstring lua_tolstring>
 foreign import ccall unsafe "lua.h lua_tolstring"
-  lua_tolstring :: LuaState -> CInt -> Ptr CSize -> IO (Ptr CChar)
+  lua_tolstring :: LuaState -> StackIndex -> Ptr CSize -> IO (Ptr CChar)
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_topointer lua_topointer>
 foreign import ccall unsafe "lua.h lua_topointer"
-  lua_topointer :: LuaState -> CInt -> IO (Ptr ())
+  lua_topointer :: LuaState -> StackIndex -> IO (Ptr ())
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_tothread lua_tothread>
 foreign import ccall unsafe "lua.h lua_tothread"
-  lua_tothread :: LuaState -> CInt -> IO LuaState
+  lua_tothread :: LuaState -> StackIndex -> IO LuaState
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_touserdata lua_touserdata>
 foreign import ccall unsafe "lua.h lua_touserdata"
-  lua_touserdata :: LuaState -> CInt -> IO (Ptr a)
+  lua_touserdata :: LuaState -> StackIndex -> IO (Ptr a)
 
 
 --
@@ -215,11 +209,11 @@
 #if LUA_VERSION_NUMBER >= 502
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawlen lua_rawlen>
 foreign import ccall unsafe "lua.h lua_rawlen"
-  lua_rawlen :: LuaState -> CInt -> IO CSize
+  lua_rawlen :: LuaState -> StackIndex -> IO CSize
 #else
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_objlen lua_objlen>
 foreign import ccall unsafe "lua.h lua_objlen"
-  lua_objlen :: LuaState -> CInt -> IO CSize
+  lua_objlen :: LuaState -> StackIndex -> IO CSize
 #endif
 
 
@@ -242,17 +236,16 @@
 foreign import ccall unsafe "lua.h lua_pushlstring"
   lua_pushlstring :: LuaState -> Ptr CChar -> CSize -> IO ()
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_pushstring lua_pushstring>
-foreign import ccall unsafe "lua.h lua_pushstring"
-  lua_pushstring :: LuaState -> Ptr CChar -> IO ()
+-- lua_pushstring is currently not supported. It's difficult to use in a haskell
+-- context.
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_pushcclosure lua_pushcclosure>
 foreign import ccall unsafe "lua.h lua_pushcclosure"
-  lua_pushcclosure :: LuaState -> CFunction -> CInt -> IO ()
+  lua_pushcclosure :: LuaState -> CFunction -> NumArgs -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_pushboolean lua_pushboolean>
 foreign import ccall unsafe "lua.h lua_pushboolean"
-  lua_pushboolean :: LuaState -> CInt -> IO ()
+  lua_pushboolean :: LuaState -> LuaBool -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_pushlightuserdata lua_pushlightuserdata>
 foreign import ccall unsafe "lua.h lua_pushlightuserdata"
@@ -266,27 +259,28 @@
 --------------------------------------------------------------------------------
 -- * Get functions
 
-#if LUA_VERSION_NUMBER >= 503
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_gettable lua_gettable>
-foreign import ccall "lua.h lua_gettable"
-  lua_gettable :: LuaState -> CInt -> IO CInt
-#else
--- | See <https://www.lua.org/manual/5.2/manual.html#lua_gettable lua_gettable>
-foreign import ccall "lua.h lua_gettable"
-  lua_gettable :: LuaState -> CInt -> IO ()
-#endif
+-- lua_gettable is unsafe, use hslua_gettable instead.
+-- lua_getfield is unsafe, use hslua_getfield instead.
+-- lua_getglobal is unsafe, use hslua_getglobal instead.
+-- lua_getfenv (5.1 only) is not supported.
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_getfield lua_getfield>
-foreign import ccall "lua.h lua_getfield"
-  lua_getfield :: LuaState -> CInt -> Ptr CChar -> IO ()
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_gettable \
+-- @lua_gettable@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_gettable"
+  hslua_gettable :: LuaState -> StackIndex -> IO CInt
 
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_getfield \
+-- @lua_getfield@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_getfield"
+  hslua_getfield :: LuaState -> StackIndex -> Ptr CChar -> IO CInt
+
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawget lua_rawget>
 foreign import ccall unsafe "lua.h lua_rawget"
-  lua_rawget :: LuaState -> CInt -> IO ()
+  lua_rawget :: LuaState -> StackIndex -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawgeti lua_rawgeti>
 foreign import ccall unsafe "lua.h lua_rawgeti"
-  lua_rawgeti :: LuaState -> CInt -> CInt -> IO ()
+  lua_rawgeti :: LuaState -> StackIndex -> CInt -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_createtable lua_createtable>
 foreign import ccall unsafe "lua.h lua_createtable"
@@ -298,122 +292,81 @@
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_getmetatable lua_getmetatable>
 foreign import ccall unsafe "lua.h lua_getmetatable"
-  lua_getmetatable :: LuaState -> CInt -> IO CInt
+  lua_getmetatable :: LuaState -> StackIndex -> IO CInt
 
-#if LUA_VERSION_NUMBER < 502
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_getfenv lua_getfenv>
-foreign import ccall "lua.h lua_getfenv"
-  lua_getfenv :: LuaState -> CInt -> IO ()
-#endif
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_getglobal \
+-- @lua_getglobal@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_getglobal"
+  hslua_getglobal :: LuaState -> Ptr CChar -> IO CInt
 
-#if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_getglobal lua_getglobal>
-foreign import ccall "lua.h lua_getglobal"
-  lua_getglobal :: LuaState -> Ptr CChar -> IO ()
-#endif
 
 --------------------------------------------------------------------------------
 -- * Set functions
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_settable lua_settable>
-foreign import ccall "lua.h lua_settable"
-  lua_settable :: LuaState -> CInt -> IO ()
+-- lua_settable is unsafe, use hslua_settable instead.
+-- lua_setfield is unsafe, use hslua_setfield instead.
+-- lua_setglobal is unsafe, use hslua_setglobal instead.
+-- lua_setfenv (5.1 only) is not supported.
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_setfield lua_setfield>
-foreign import ccall "lua.h lua_setfield"
-  lua_setfield :: LuaState -> CInt -> Ptr CChar -> IO ()
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_settable \
+-- @lua_settable@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_settable"
+  hslua_settable :: LuaState -> StackIndex -> IO CInt
 
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_setfield \
+-- @lua_setfield@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_setfield"
+  hslua_setfield :: LuaState -> StackIndex -> Ptr CChar -> IO CInt
+
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawset lua_rawset>
 foreign import ccall unsafe "lua.h lua_rawset"
-  lua_rawset :: LuaState -> CInt -> IO ()
+  lua_rawset :: LuaState -> StackIndex -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_rawseti lua_rawseti>
 foreign import ccall unsafe "lua.h lua_rawseti"
-  lua_rawseti :: LuaState -> CInt -> CInt -> IO ()
+  lua_rawseti :: LuaState -> StackIndex -> CInt -> IO ()
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_setmetatable lua_setmetatable>
 foreign import ccall unsafe "lua.h lua_setmetatable"
-  lua_setmetatable :: LuaState -> CInt -> IO ()
+  lua_setmetatable :: LuaState -> StackIndex -> IO ()
 
-#if LUA_VERSION_NUMBER < 502
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_setfenv lua_setfenv>
-foreign import ccall "lua.h lua_setfenv"
-  lua_setfenv :: LuaState -> CInt -> IO CInt
-#endif
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_setglobal \
+-- @lua_setglobal@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_setglobal"
+  hslua_setglobal :: LuaState -> Ptr CChar -> IO CInt
 
-#if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_setglobal lua_setglobal>
-foreign import ccall "lua.h lua_setglobal"
-  lua_setglobal :: LuaState -> Ptr CChar -> IO ()
-#endif
 
 --------------------------------------------------------------------------------
 -- * 'load' and 'call' functions (load and run Lua code)
 
 #if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_callk lua_callk>
-foreign import ccall "lua.h lua_callk"
-  lua_callk :: LuaState -> CInt -> CInt -> CInt -> Ptr () -> IO ()
-#else
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_call lua_call>
-foreign import ccall "lua.h lua_call"
-  lua_call :: LuaState -> CInt -> CInt -> IO ()
-#endif
-
-#if LUA_VERSION_NUMBER >= 502
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_pcallk lua_pcallk>
 foreign import ccall "lua.h lua_pcallk"
-  lua_pcallk :: LuaState -> CInt -> CInt -> CInt -> CInt -> Ptr () -> IO CInt
+  lua_pcallk :: LuaState -> NumArgs -> NumResults -> StackIndex
+             -> CInt -> Ptr () -> IO StatusCode
 #else
 -- | See <https://www.lua.org/manual/5.1/manual.html#lua_pcall lua_pcall>
 foreign import ccall "lua.h lua_pcall"
-  lua_pcall :: LuaState -> CInt -> CInt -> CInt -> IO CInt
-#endif
-
-#if LUA_VERSION_NUMBER < 502
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_cpcall lua_cpcall>
-foreign import ccall "lua.h lua_cpcall"
-  lua_cpcall :: LuaState -> CFunction -> Ptr a -> IO CInt
+  lua_pcall :: LuaState -> NumArgs -> NumResults -> StackIndex
+            -> IO StatusCode
 #endif
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_load lua_load>
-foreign import ccall "lua.h lua_load"
-  lua_load :: LuaState -> FunPtr LuaReader -> Ptr () -> Ptr CChar -> IO CInt
-
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_dump lua_dump>
-foreign import ccall "lua.h lua_dump"
-  lua_dump :: LuaState -> FunPtr LuaWriter -> Ptr () -> IO ()
+-- currently unsupported:
+-- lua_load
+-- lua_dump
 
 
---------------------------------------------------------------------------------
+------------------------------------------------------------------------------
 -- * Coroutine functions
 
-#if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_yieldk lua_yieldk>
-foreign import ccall "lua.h lua_yieldk"
-  lua_yieldk :: LuaState -> CInt -> CInt -> Ptr () -> IO CInt
-#else
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_yield lua_yield>
-foreign import ccall "lua.h lua_yield"
-  lua_yield :: LuaState -> CInt -> IO CInt
-#endif
-
-#if LUA_VERSION_NUMBER >= 502
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_resume lua_resume>
-foreign import ccall "lua.h lua_resume"
-  lua_resume :: LuaState -> CInt -> CInt -> IO CInt
-#else
--- | See <https://www.lua.org/manual/5.1/manual.html#lua_resume lua_resume>
-foreign import ccall "lua.h lua_resume"
-  lua_resume :: LuaState -> CInt -> IO CInt
-#endif
+-- lua_yield / lua_yieldk and lua_resume are currently not supported.
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_status lua_status>
 foreign import ccall unsafe "lua.h lua_status"
-  lua_status :: LuaState -> IO CInt
+  lua_status :: LuaState -> IO StatusCode
 
 
---------------------------------------------------------------------------------
+------------------------------------------------------------------------------
 -- * Garbage-collection functions and options
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#lua_gc lua_gc>
@@ -421,88 +374,62 @@
   lua_gc :: LuaState -> CInt -> CInt -> IO CInt
 
 
---------------------------------------------------------------------------------
+------------------------------------------------------------------------------
 -- * Miscellaneous functions
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_error lua_error>
-foreign import ccall unsafe "lua.h lua_error"
-  lua_error :: LuaState -> IO CInt
+-- lua_error is unsafe in a haskell context and hence not supported.
+-- lua_next is unsafe, use hslua_next instead.
+-- lua_concat is unsafe (may trigger a longjmp), use hslua_concat instead.
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_next lua_next>
-foreign import ccall "lua.h lua_next"
-  lua_next :: LuaState -> CInt -> IO CInt
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_next \
+-- @lua_next@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_next"
+  hslua_next :: LuaState -> StackIndex -> IO (Failable LuaBool)
 
--- | See <https://www.lua.org/manual/5.3/manual.html#lua_concat lua_concat>
-foreign import ccall "lua.h lua_concat"
-  lua_concat :: LuaState -> CInt -> IO ()
+-- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_concat \
+-- @lua_concat@> which catches any @longjmp@s.
+foreign import ccall "safer-api.h hslua_concat"
+  hslua_concat :: LuaState -> NumArgs -> IO (Failable LuaBool)
 
+
+------------------------------------------------------------------------------
+-- * Lua Libraries
+
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_openlibs luaL_openlibs>
 foreign import ccall unsafe "lualib.h luaL_openlibs"
   luaL_openlibs :: LuaState -> IO ()
 
--- | Opens the base library.
-foreign import ccall unsafe "lualib.h luaopen_base"
-  lua_open_base :: LuaState -> IO CInt
-
--- | Function pointer to @'lua_open_base'@
+-- | Point to function opening the base library.
 foreign import ccall unsafe "lualib.h &luaopen_base"
-  lua_open_base_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the table library.
-foreign import ccall unsafe "lualib.h luaopen_table"
-  lua_open_table :: LuaState -> IO CInt
+  lua_open_base_ptr :: CFunction
 
--- | Function pointer to @'lua_open_table'@
+-- | Point to function opening the table library.
 foreign import ccall unsafe "lualib.h &luaopen_table"
-  lua_open_table_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the io library.
-foreign import ccall unsafe "lualib.h luaopen_io"
-  lua_open_io :: LuaState -> IO CInt
+  lua_open_table_ptr :: CFunction
 
--- | Function pointer to @'lua_open_io'@
+-- | Point to function opening the io library.
 foreign import ccall unsafe "lualib.h &luaopen_io"
-  lua_open_io_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the os library.
-foreign import ccall unsafe "lualib.h luaopen_os"
-  lua_open_os :: LuaState -> IO CInt
+  lua_open_io_ptr :: CFunction
 
--- | Function pointer to @'lua_open_os'@
+-- | Point to function opening the os library.
 foreign import ccall unsafe "lualib.h &luaopen_os"
-  lua_open_os_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the string library.
-foreign import ccall unsafe "lualib.h luaopen_string"
-  lua_open_string :: LuaState -> IO CInt
+  lua_open_os_ptr :: CFunction
 
--- | Function pointer to @'lua_open_string'@
+-- | Point to function opening the string library.
 foreign import ccall unsafe "lualib.h &luaopen_string"
-  lua_open_string_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the math library.
-foreign import ccall unsafe "lualib.h luaopen_math"
-  lua_open_math :: LuaState -> IO CInt
+  lua_open_string_ptr :: CFunction
 
--- | Function pointer to @'lua_open_math'@
+-- | Point to function opening the math library.
 foreign import ccall unsafe "lualib.h &luaopen_math"
-  lua_open_math_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the debug library.
-foreign import ccall unsafe "lualib.h luaopen_debug"
-  lua_open_debug :: LuaState -> IO CInt
+  lua_open_math_ptr :: CFunction
 
--- | Function pointer to @'lua_open_debug'@
+-- | Point to function opening the debug library.
 foreign import ccall unsafe "lualib.h &luaopen_debug"
-  lua_open_debug_ptr :: FunPtr (LuaState -> IO CInt)
-
--- | Opens the package library.
-foreign import ccall unsafe "lualib.h luaopen_package"
-  lua_open_package :: LuaState -> IO CInt
+  lua_open_debug_ptr :: CFunction
 
--- | Function pointer to @'lua_open_package'@
+-- | Point to function opening the package library.
 foreign import ccall unsafe "lualib.h &luaopen_package"
-  lua_open_package_ptr :: FunPtr (LuaState -> IO CInt)
+  lua_open_package_ptr :: CFunction
 
 
 --------------------------------------------------------------------------------
@@ -514,30 +441,26 @@
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_newmetatable luaL_newmetatable>
 foreign import ccall "lauxlib.h luaL_newmetatable"
-  luaL_newmetatable :: LuaState -> Ptr CChar -> IO CInt
-
--- | See <https://www.lua.org/manual/5.3/manual.html#luaL_argerror luaL_argerror>
-foreign import ccall "lauxlib.h luaL_argerror"
-  luaL_argerror :: LuaState -> CInt -> Ptr CChar -> IO CInt
+  luaL_newmetatable :: LuaState -> Ptr CChar -> IO LuaBool
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_ref luaL_ref>
 foreign import ccall "lauxlib.h luaL_ref"
-  luaL_ref :: LuaState -> CInt -> IO CInt
+  luaL_ref :: LuaState -> StackIndex -> IO CInt
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_unref luaL_unref>
 foreign import ccall "lauxlib.h luaL_unref"
-  luaL_unref :: LuaState -> CInt -> CInt -> IO ()
+  luaL_unref :: LuaState -> StackIndex -> CInt -> IO ()
 
 #if LUA_VERSION_NUMBER >= 502
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_loadfilex luaL_loadfilex>
 foreign import ccall "lauxlib.h luaL_loadfilex"
-  luaL_loadfilex :: LuaState -> Ptr CChar -> Ptr CChar -> IO CInt
+  luaL_loadfilex :: LuaState -> Ptr CChar -> Ptr CChar -> IO StatusCode
 #else
 -- | See <https://www.lua.org/manual/5.1/manual.html#luaL_loadfile luaL_loadfile>
 foreign import ccall "lauxlib.h luaL_loadfile"
-  luaL_loadfile :: LuaState -> Ptr CChar -> IO CInt
+  luaL_loadfile :: LuaState -> Ptr CChar -> IO StatusCode
 #endif
 
 -- | See <https://www.lua.org/manual/5.3/manual.html#luaL_loadstring luaL_loadstring>
 foreign import ccall unsafe "lauxlib.h luaL_loadstring"
-  luaL_loadstring :: LuaState -> Ptr CChar -> IO CInt
+  luaL_loadstring :: LuaState -> Ptr CChar -> IO StatusCode
diff --git a/src/Foreign/Lua/Api/SafeBindings.hsc b/src/Foreign/Lua/Api/SafeBindings.hsc
deleted file mode 100644
--- a/src/Foreign/Lua/Api/SafeBindings.hsc
+++ /dev/null
@@ -1,73 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE ForeignFunctionInterface #-}
-{-|
-Module      : Foreign.Lua.Api.SafeBindings
-Copyright   : © 2017 Albert Krewinkel
-License     : MIT
-Maintainer  : Albert Krewinkel <tarleb+hslua@zeitkraut.de>
-Stability   : beta
-Portability : ForeignFunctionInterface
-
-Haskell bindings to safe, @longjmp@ catching wrappers of Lua C API functions.
-
-Functions in this module follow a custom protocol. If an error happens (i.e., if
-the lua status is not 0), the status code is returned as a negative number.
-Results of a successful execution of the wrapped function are returned as
-non-negative numbers. This is possible because all (supported) lua API functions
-are either safe and don't need wrapping, or return @void@, @size_t@ or a
-non-negative @int@.
--}
-module Foreign.Lua.Api.SafeBindings where
-
-import Foreign.C
-import Foreign.Lua.Api.Types
-import Foreign.Ptr
-
-#include "safer-api.h"
-
-#if LUA_VERSION_NUM >= 502
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_compare \
--- @lua_compare@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_compare"
-  hslua_compare :: LuaState -> CInt -> CInt -> CInt -> IO CInt
-#endif
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_concat \
--- @lua_concat@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_concat"
-  hslua_concat :: LuaState -> CInt -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_getfield \
--- @lua_getfield@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_getfield"
-  hslua_getfield :: LuaState -> CInt -> Ptr CChar -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_getglobal \
--- @lua_getglobal@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_getglobal"
-  hslua_getglobal :: LuaState -> Ptr CChar -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_gettable \
--- @lua_gettable@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_gettable"
-  hslua_gettable :: LuaState -> CInt -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_next \
--- @lua_next@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_next"
-  hslua_next :: LuaState -> CInt -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_setfield \
--- @lua_setfield@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_setfield"
-  hslua_setfield :: LuaState -> CInt -> Ptr CChar -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_setglobal \
--- @lua_setglobal@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_setglobal"
-  hslua_setglobal :: LuaState -> Ptr CChar -> IO CInt
-
--- | Wrapper around <https://lua.org/manual/5.3/manual.html#lua_settable \
--- @lua_settable@> which catches any @longjmp@s.
-foreign import ccall "safer-api.h hslua_settable"
-  hslua_settable :: LuaState -> CInt -> IO CInt
diff --git a/src/Foreign/Lua/Api/Types.hsc b/src/Foreign/Lua/Api/Types.hsc
--- a/src/Foreign/Lua/Api/Types.hsc
+++ b/src/Foreign/Lua/Api/Types.hsc
@@ -39,11 +39,15 @@
 -}
 module Foreign.Lua.Api.Types where
 
-import Data.Int
-import Foreign.C
-import Foreign.Ptr
+import Prelude hiding (EQ, LT)
+import Data.Int (#{type LUA_INTEGER})
+import Foreign.C (CInt)
+import Foreign.Ptr (FunPtr, Ptr)
+import Foreign.Storable (Storable)
 
 #include "lua.h"
+-- required only for LUA_ERRFILE
+#include "lauxlib.h"
 
 -- | An opaque structure that points to a thread and indirectly (through the
 -- thread) to the whole state of a Lua interpreter. The Lua library is fully
@@ -53,23 +57,6 @@
 -- Synonym for @lua_State *@. See <https://www.lua.org/manual/5.3/#lua_State lua_State>.
 newtype LuaState = LuaState (Ptr ()) deriving (Eq)
 
--- | Synonym for @lua_Alloc@. See <https://www.lua.org/manual/5.3/#lua_Alloc lua_Alloc>.
-type LuaAlloc = Ptr () -> Ptr () -> CSize -> CSize -> IO (Ptr ())
-
--- | The reader function used by @'lua_load'@. Every time it needs another piece
--- of the chunk, @'lua_load'@ calls the reader, passing along its data
--- parameter. The reader must return a pointer to a block of memory with a new
--- piece of the chunk and set size to the block size. The block must exist until
--- the reader function is called again. To signal the end of the chunk, the
--- reader must return NULL or set size to zero. The reader function may return
--- pieces of any size greater than zero.
---
--- See <https://www.lua.org/manual/5.3/#lua_Reader lua_Reader>.
-type LuaReader = Ptr () -> Ptr () -> Ptr CSize -> IO (Ptr CChar)
-
--- | Synonym for @lua_Writer@. See <https://www.lua.org/manual/5.3/#lua_Writer lua_Writer>.
-type LuaWriter = LuaState -> Ptr CChar -> CSize -> Ptr () -> IO CInt
-
 -- |  Type for C functions.
 --
 -- In order to communicate properly with Lua, a C function must use the
@@ -85,7 +72,7 @@
 -- can also return many results.
 --
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_CFunction lua_CFunction>.
-type CFunction = FunPtr (LuaState -> IO CInt)
+type CFunction = FunPtr (LuaState -> IO NumResults)
 
 -- |  The type of integers in Lua.
 --
@@ -103,99 +90,155 @@
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_Number lua_Number>.
 type LuaNumber = #{type LUA_NUMBER}
 
+
+--
+-- LuaBool
+--
+
+-- | Boolean value returned by a Lua C API function. This is a @'CInt'@ and
+-- interpreted as @'False'@ iff the value is @0@, @'True'@ otherwise.
+newtype LuaBool = LuaBool CInt
+  deriving (Eq, Storable)
+
+-- | Convert a @'LuaBool'@ to a Haskell @'Bool'@.
+fromLuaBool :: LuaBool -> Bool
+fromLuaBool (LuaBool 0) = False
+fromLuaBool _           = True
+
+-- | Convert a Haskell @'Bool'@ to a @'LuaBool'@.
+toLuaBool :: Bool -> LuaBool
+toLuaBool True  = LuaBool 1
+toLuaBool False = LuaBool 0
+
+
+--
+-- * Type of Lua values
+--
+
 -- | Enumeration used as type tag.
 -- See <https://www.lua.org/manual/5.3/manual.html#lua_type lua_type>.
-data LTYPE
-  = TNONE           -- ^ non-valid stack index
-  | TNIL            -- ^ type of lua's @nil@ value
-  | TBOOLEAN        -- ^ type of lua booleans
-  | TLIGHTUSERDATA  -- ^ type of light userdata
-  | TNUMBER         -- ^ type of lua numbers. See @'LuaNumber'@
-  | TSTRING         -- ^ type of lua string values
-  | TTABLE          -- ^ type of lua tables
-  | TFUNCTION       -- ^ type of functions, either normal or @'CFunction'@
-  | TUSERDATA       -- ^ type of full user data
-  | TTHREAD         -- ^ type of lua threads
+data Type
+  = TypeNone           -- ^ non-valid stack index
+  | TypeNil            -- ^ type of lua's @nil@ value
+  | TypeBoolean        -- ^ type of lua booleans
+  | TypeLightUserdata  -- ^ type of light userdata
+  | TypeNumber         -- ^ type of lua numbers. See @'LuaNumber'@
+  | TypeString         -- ^ type of lua string values
+  | TypeTable          -- ^ type of lua tables
+  | TypeFunction       -- ^ type of functions, either normal or @'CFunction'@
+  | TypeUserdata       -- ^ type of full user data
+  | TypeThread         -- ^ type of lua threads
   deriving (Bounded, Eq, Ord, Show)
 
-instance Enum LTYPE where
-  fromEnum TNONE          = #{const LUA_TNONE}
-  fromEnum TNIL           = #{const LUA_TNIL}
-  fromEnum TBOOLEAN       = #{const LUA_TBOOLEAN}
-  fromEnum TLIGHTUSERDATA = #{const LUA_TLIGHTUSERDATA}
-  fromEnum TNUMBER        = #{const LUA_TNUMBER}
-  fromEnum TSTRING        = #{const LUA_TSTRING}
-  fromEnum TTABLE         = #{const LUA_TTABLE}
-  fromEnum TFUNCTION      = #{const LUA_TFUNCTION}
-  fromEnum TUSERDATA      = #{const LUA_TUSERDATA}
-  fromEnum TTHREAD        = #{const LUA_TTHREAD}
+-- | Integer code used to encode the type of a lua value.
+newtype TypeCode = TypeCode { fromTypeCode :: CInt }
+  deriving (Eq, Ord, Show)
 
-  toEnum (#{const LUA_TNONE})          = TNONE
-  toEnum (#{const LUA_TNIL})           = TNIL
-  toEnum (#{const LUA_TBOOLEAN})       = TBOOLEAN
-  toEnum (#{const LUA_TLIGHTUSERDATA}) = TLIGHTUSERDATA
-  toEnum (#{const LUA_TNUMBER})        = TNUMBER
-  toEnum (#{const LUA_TSTRING})        = TSTRING
-  toEnum (#{const LUA_TTABLE})         = TTABLE
-  toEnum (#{const LUA_TFUNCTION})      = TFUNCTION
-  toEnum (#{const LUA_TUSERDATA})      = TUSERDATA
-  toEnum (#{const LUA_TTHREAD})        = TTHREAD
-  toEnum n                             = error $ "Cannot convert (" ++ show n ++ ") to LTYPE"
+instance Enum Type where
+  fromEnum = fromIntegral . fromTypeCode . fromType
+  toEnum = toType . TypeCode . fromIntegral
 
--- | Convert number to lua type.
-toLuaType :: CInt -> LTYPE
-toLuaType = toEnum . fromIntegral
+-- | Convert a lua Type to a type code which can be passed to the C API.
+fromType :: Type -> TypeCode
+fromType tp = TypeCode $ case tp of
+  TypeNone          -> #{const LUA_TNONE}
+  TypeNil           -> #{const LUA_TNIL}
+  TypeBoolean       -> #{const LUA_TBOOLEAN}
+  TypeLightUserdata -> #{const LUA_TLIGHTUSERDATA}
+  TypeNumber        -> #{const LUA_TNUMBER}
+  TypeString        -> #{const LUA_TSTRING}
+  TypeTable         -> #{const LUA_TTABLE}
+  TypeFunction      -> #{const LUA_TFUNCTION}
+  TypeUserdata      -> #{const LUA_TUSERDATA}
+  TypeThread        -> #{const LUA_TTHREAD}
 
--- | Convert Lua type to its C representation.
-fromLuaType :: LTYPE -> CInt
-fromLuaType = fromIntegral . fromEnum
+-- | Convert numerical code to lua type.
+toType :: TypeCode -> Type
+toType (TypeCode c) = case c of
+  (#{const LUA_TNONE})          -> TypeNone
+  (#{const LUA_TNIL})           -> TypeNil
+  (#{const LUA_TBOOLEAN})       -> TypeBoolean
+  (#{const LUA_TLIGHTUSERDATA}) -> TypeLightUserdata
+  (#{const LUA_TNUMBER})        -> TypeNumber
+  (#{const LUA_TSTRING})        -> TypeString
+  (#{const LUA_TTABLE})         -> TypeTable
+  (#{const LUA_TFUNCTION})      -> TypeFunction
+  (#{const LUA_TUSERDATA})      -> TypeUserdata
+  (#{const LUA_TTHREAD})        -> TypeThread
+  _ -> error ("No Type corresponding to " ++ show c)
 
+--
+-- * Relational Operator
+--
+
 -- | Lua comparison operations.
-data LuaRelation
-  = LuaEQ -- ^ Correponds to lua's equality (==) operator.
-  | LuaLT -- ^ Correponds to lua's strictly-lesser-than (<) operator
-  | LuaLE -- ^ Correponds to lua's lesser-or-equal (<=) operator
+data RelationalOperator
+  = EQ -- ^ Correponds to lua's equality (==) operator.
+  | LT -- ^ Correponds to lua's strictly-lesser-than (<) operator
+  | LE -- ^ Correponds to lua's lesser-or-equal (<=) operator
   deriving (Eq, Ord, Show)
 
 -- | Convert relation operator to its C representation.
-fromLuaRelation :: LuaRelation -> CInt
+fromRelationalOperator :: RelationalOperator -> CInt
 #if LUA_VERSION_NUMBER >= 502
-fromLuaRelation LuaEQ = #{const LUA_OPEQ}
-fromLuaRelation LuaLT = #{const LUA_OPLT}
-fromLuaRelation LuaLE = #{const LUA_OPLE}
+fromRelationalOperator EQ = #{const LUA_OPEQ}
+fromRelationalOperator LT = #{const LUA_OPLT}
+fromRelationalOperator LE = #{const LUA_OPLE}
 #else
-fromLuaRelation LuaEQ = 0
-fromLuaRelation LuaLT = 1
-fromLuaRelation LuaLE = 2
+fromRelationalOperator EQ = 0
+fromRelationalOperator LT = 1
+fromRelationalOperator LE = 2
 #endif
 
+
+--
+-- * Status
+--
+
 -- | Lua status values.
-data LuaStatus
-  = LuaOK        -- ^ success
-  | LuaYield     -- ^ yielding / suspended coroutine
-  | LuaErrRun    -- ^ a runtime rror
-  | LuaErrSyntax -- ^ syntax error during precompilation
-  | LuaErrMem    -- ^ memory allocation (out-of-memory) error.
-  | LuaErrErr    -- ^ error while running the message handler.
-  | LuaErrGcmm   -- ^ error while running a @__gc@ metamethod.
+data Status
+  = OK        -- ^ success
+  | Yield     -- ^ yielding / suspended coroutine
+  | ErrRun    -- ^ a runtime rror
+  | ErrSyntax -- ^ syntax error during precompilation
+  | ErrMem    -- ^ memory allocation (out-of-memory) error.
+  | ErrErr    -- ^ error while running the message handler.
+  | ErrGcmm   -- ^ error while running a @__gc@ metamethod.
+  | ErrFile   -- ^ opening or reading a file failed.
   deriving (Eq, Show)
 
 -- | Convert C integer constant to @'LuaStatus'@.
-toLuaStatus :: CInt -> LuaStatus
--- LUA_OK is not defined in Lua 5.1
-toLuaStatus 0                        = LuaOK
-toLuaStatus (#{const LUA_YIELD})     = LuaYield
-toLuaStatus (#{const LUA_ERRRUN})    = LuaErrRun
-toLuaStatus (#{const LUA_ERRSYNTAX}) = LuaErrSyntax
-toLuaStatus (#{const LUA_ERRMEM})    = LuaErrMem
--- LUA_ERRGCMM did not exist in Lua 5.1; comes before LUA_ERRERR when defined
+toStatus :: StatusCode -> Status
+toStatus (StatusCode c) = case c of
+  -- LUA_OK is not defined in Lua 5.1
+  0                        -> OK
+  (#{const LUA_YIELD})     -> Yield
+  (#{const LUA_ERRRUN})    -> ErrRun
+  (#{const LUA_ERRSYNTAX}) -> ErrSyntax
+  (#{const LUA_ERRMEM})    -> ErrMem
+  -- LUA_ERRGCMM did not exist in Lua 5.1; comes before LUA_ERRERR when defined
 #if LUA_VERSION_NUMBER >= 502
-toLuaStatus (#{const LUA_ERRGCMM})   = LuaErrGcmm
-toLuaStatus (#{const LUA_ERRERR})    = LuaErrErr
+  (#{const LUA_ERRGCMM})   -> ErrGcmm
+  (#{const LUA_ERRERR})    -> ErrErr
 #else
-toLuaStatus (#{const LUA_ERRERR})    = LuaErrErr
+  (#{const LUA_ERRERR})    -> ErrErr
 #endif
-toLuaStatus n = error $ "Cannot convert (" ++ show n ++ ") to LuaStatus"
+  (#{const LUA_ERRFILE})   -> ErrFile
+  n -> error $ "Cannot convert (" ++ show n ++ ") to LuaStatus"
+
+-- | Integer code used to signal the status of a thread or computation.
+-- See @'Status'@.
+newtype StatusCode = StatusCode CInt deriving Eq
+
+-- | Value or an error, using the convention that value below zero indicate an
+-- error. Values greater than zero are used verbatim. The phantom type is
+-- currently used for documentation only and has no effect.
+type Failable a = CInt
+
+
+--
+-- * Gargabe Collection Control
+--
 
 -- | Enumeration used by @gc@ function.
 data GCCONTROL
diff --git a/src/Foreign/Lua/FunctionCalling.hs b/src/Foreign/Lua/FunctionCalling.hs
--- a/src/Foreign/Lua/FunctionCalling.hs
+++ b/src/Foreign/Lua/FunctionCalling.hs
@@ -133,7 +133,7 @@
     getglobal' fnName
     x
     z <- pcall nargs 1 Nothing
-    if z /= LuaOK
+    if z /= OK
       then tostring (-1) >>= throwLuaError . unpack
       else peek (-1) <* pop 1
 
diff --git a/src/Foreign/Lua/Types/FromLuaStack.hs b/src/Foreign/Lua/Types/FromLuaStack.hs
--- a/src/Foreign/Lua/Types/FromLuaStack.hs
+++ b/src/Foreign/Lua/Types/FromLuaStack.hs
@@ -44,6 +44,7 @@
   , Result
   , peekEither
   , pairsFromTable
+  , toList
   ) where
 
 import Data.ByteString (ByteString)
@@ -60,12 +61,34 @@
 -- | Result returned when trying to get a value from the lua stack.
 type Result a = Either String a
 
+-- | Use @test@ to check whether the value at stack index @n@ has the correct
+-- type and use @peekfn@ to convert it to a haskell value if possible. A
+-- successfully received value is wrapped using the @'Success'@ constructor,
+-- while a type mismatch results in an @Error@ with the given error message.
+typeChecked :: String
+            -> (StackIndex -> Lua Bool)
+            -> (StackIndex -> Lua a)
+            -> StackIndex
+            -> Lua a
+typeChecked expectedType test peekfn n = do
+  v <- test n
+  if v
+    then peekfn n
+    else do
+      actual <- ltype n >>= typename
+      throwLuaError $ "Expected a " <> expectedType <> " but got a " <> actual
+
 -- | A value that can be read from the Lua stack.
 class FromLuaStack a where
   -- | Check if at index @n@ there is a convertible Lua value and if so return
   -- it.  Throws a @'LuaException'@ otherwise.
   peek :: StackIndex -> Lua a
 
+-- | Try to convert the value at the given stack index to a haskell value.
+-- Returns @Left@ with an error message on failure.
+peekEither :: FromLuaStack a => StackIndex -> Lua (Either String a)
+peekEither idx = catchLuaError (return <$> peek idx) (return . Left . show)
+
 instance FromLuaStack () where
   peek = typeChecked "nil" isnil (const $ return ())
 
@@ -104,17 +127,21 @@
   peek = fmap T.unpack . peek
 
 instance FromLuaStack a => FromLuaStack [a] where
-  peek n = catchLuaError (go . enumFromTo 1 =<< rawlen n) amendError
-   where
-    go [] = return []
-    go (i : is) = do
-      ret <- rawgeti n i *> peek (-1) <* pop 1
-      (ret:) <$> go is
-    amendError err = throwLuaError ("Could not read list: " ++ show err)
+  peek = toList
 
 instance (Ord a, FromLuaStack a, FromLuaStack b) => FromLuaStack (Map a b) where
   peek idx = fromList <$> pairsFromTable idx
 
+-- | Read a table into a list
+toList :: FromLuaStack a => StackIndex -> Lua [a]
+toList n = (go . enumFromTo 1 =<< rawlen n) `catchLuaError` amendError
+ where
+  go [] = return []
+  go (i : is) = do
+    ret <- rawgeti n i *> peek (-1) <* pop 1
+    (ret:) <$> go is
+  amendError err = throwLuaError ("Could not read list: " ++ show err)
+
 -- | Read a table into a list of pairs.
 pairsFromTable :: (FromLuaStack a, FromLuaStack b) => StackIndex -> Lua [(a, b)]
 pairsFromTable idx = do
@@ -140,24 +167,101 @@
       return (Just (k, v))
     else return Nothing
 
--- | Use @test@ to check whether the value at stack index @n@ has the correct
--- type and use @peekfn@ to convert it to a haskell value if possible. A
--- successfully received value is wrapped using the @'Success'@ constructor,
--- while a type mismatch results in an @Error@ with the given error message.
-typeChecked :: String
-            -> (StackIndex -> Lua Bool)
-            -> (StackIndex -> Lua a)
-            -> StackIndex
-            -> Lua a
-typeChecked expectedType test peekfn n = do
-  v <- test n
-  if v
-    then peekfn n
-    else do
-      actual <- ltype n >>= typename
-      throwLuaError $ "Expected a " <> expectedType <> " but got a " <> actual
+--
+-- Tuples
+--
 
--- | Try to convert the value at the given stack index to a haskell value.
--- Returns @Left@ with an error message on failure.
-peekEither :: FromLuaStack a => StackIndex -> Lua (Either String a)
-peekEither idx = catchLuaError (return <$> peek idx) (return . Left . show)
+instance (FromLuaStack a, FromLuaStack b) => FromLuaStack (a, b) where
+  peek idx = do
+    a <- rawgeti idx 1 *> peek (-1) <* pop 1
+    b <- rawgeti idx 2 *> peek (-1) <* pop 1
+    return (a, b)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c) =>
+         FromLuaStack (a, b, c)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    pop 4
+    return (a, b, c)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c, FromLuaStack d) =>
+         FromLuaStack (a, b, c, d)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    d <- getTableIndex 4
+    pop 5
+    return (a, b, c, d)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c,
+          FromLuaStack d, FromLuaStack e) =>
+         FromLuaStack (a, b, c, d, e)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    d <- getTableIndex 4
+    e <- getTableIndex 5
+    pop 6
+    return (a, b, c, d, e)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c,
+          FromLuaStack d, FromLuaStack e, FromLuaStack f) =>
+         FromLuaStack (a, b, c, d, e, f)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    d <- getTableIndex 4
+    e <- getTableIndex 5
+    f <- getTableIndex 6
+    pop 7
+    return (a, b, c, d, e, f)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c, FromLuaStack d,
+          FromLuaStack e, FromLuaStack f, FromLuaStack g) =>
+         FromLuaStack (a, b, c, d, e, f, g)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    d <- getTableIndex 4
+    e <- getTableIndex 5
+    f <- getTableIndex 6
+    g <- getTableIndex 7
+    pop 8
+    return (a, b, c, d, e, f, g)
+
+instance (FromLuaStack a, FromLuaStack b, FromLuaStack c, FromLuaStack d,
+          FromLuaStack e, FromLuaStack f, FromLuaStack g, FromLuaStack h) =>
+         FromLuaStack (a, b, c, d, e, f, g, h)
+ where
+  peek idx = do
+    pushvalue idx
+    a <- getTableIndex 1
+    b <- getTableIndex 2
+    c <- getTableIndex 3
+    d <- getTableIndex 4
+    e <- getTableIndex 5
+    f <- getTableIndex 6
+    g <- getTableIndex 7
+    h <- getTableIndex 8
+    pop 9
+    return (a, b, c, d, e, f, g, h)
+
+-- | Helper function to get the nth table value
+getTableIndex :: FromLuaStack b => Int -> Lua b
+getTableIndex key = rawgeti (StackIndex (- (fromIntegral key))) key *> peek (-1)
diff --git a/src/Foreign/Lua/Types/ToLuaStack.hs b/src/Foreign/Lua/Types/ToLuaStack.hs
--- a/src/Foreign/Lua/Types/ToLuaStack.hs
+++ b/src/Foreign/Lua/Types/ToLuaStack.hs
@@ -41,6 +41,7 @@
 -}
 module Foreign.Lua.Types.ToLuaStack
   ( ToLuaStack (..)
+  , pushList
   ) where
 
 import Control.Monad (zipWithM_)
@@ -94,13 +95,105 @@
   push = push . T.pack
 
 instance ToLuaStack a => ToLuaStack [a] where
-  push xs = do
-    let setField i x = push x *> rawseti (-2) i
-    newtable
-    zipWithM_ setField [1..] xs
+  push = pushList
 
+-- | Push list as numerically indexed table.
+pushList :: ToLuaStack a => [a] -> Lua ()
+pushList xs = do
+  let setField i x = push x *> rawseti (-2) i
+  newtable
+  zipWithM_ setField [1..] xs
+
 instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (Map a b) where
   push m = do
     let addValue (k, v) = push k *> push v *> rawset (-3)
     newtable
     mapM_ addValue (toList m)
+
+--
+-- Tuples
+--
+instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (a, b) where
+  push (a, b) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c) =>
+         ToLuaStack (a, b, c)
+ where
+  push (a, b, c) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c, ToLuaStack d) =>
+         ToLuaStack (a, b, c, d)
+ where
+  push (a, b, c, d) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+    addRawInt 4 d
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c,
+          ToLuaStack d, ToLuaStack e) =>
+         ToLuaStack (a, b, c, d, e)
+ where
+  push (a, b, c, d, e) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+    addRawInt 4 d
+    addRawInt 5 e
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c,
+          ToLuaStack d, ToLuaStack e, ToLuaStack f) =>
+         ToLuaStack (a, b, c, d, e, f)
+ where
+  push (a, b, c, d, e, f) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+    addRawInt 4 d
+    addRawInt 5 e
+    addRawInt 6 f
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c, ToLuaStack d,
+          ToLuaStack e, ToLuaStack f, ToLuaStack g) =>
+         ToLuaStack (a, b, c, d, e, f, g)
+ where
+  push (a, b, c, d, e, f, g) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+    addRawInt 4 d
+    addRawInt 5 e
+    addRawInt 6 f
+    addRawInt 7 g
+
+instance (ToLuaStack a, ToLuaStack b, ToLuaStack c, ToLuaStack d,
+          ToLuaStack e, ToLuaStack f, ToLuaStack g, ToLuaStack h) =>
+         ToLuaStack (a, b, c, d, e, f, g, h)
+ where
+  push (a, b, c, d, e, f, g, h) = do
+    newtable
+    addRawInt 1 a
+    addRawInt 2 b
+    addRawInt 3 c
+    addRawInt 4 d
+    addRawInt 5 e
+    addRawInt 6 f
+    addRawInt 7 g
+    addRawInt 8 h
+
+-- | Set numeric key/value in table at the top of the stack.
+addRawInt :: ToLuaStack a => Int -> a -> Lua ()
+addRawInt idx val = do
+  push val
+  rawseti (-2) idx
diff --git a/test/Foreign/Lua/ApiTest.hs b/test/Foreign/Lua/ApiTest.hs
--- a/test/Foreign/Lua/ApiTest.hs
+++ b/test/Foreign/Lua/ApiTest.hs
@@ -38,7 +38,7 @@
 import Prelude hiding (compare)
 
 import Control.Monad (forM_)
-import Foreign.Lua
+import Foreign.Lua as Lua
 import Test.HsLua.Util (luaTestCase, pushLuaExpr)
 import Test.QuickCheck (Property, (.&&.))
 import Test.QuickCheck.Arbitrary (Arbitrary (..), arbitraryBoundedEnum)
@@ -48,6 +48,7 @@
 import Test.Tasty.QuickCheck (testProperty)
 
 import qualified Prelude
+import qualified Foreign.Lua.Api.RawBindings as LuaRaw
 
 
 -- | Specifications for Attributes parsing functions.
@@ -93,16 +94,56 @@
       slen <- strlen (-1)
       return $ rlen == olen && rlen == slen && rlen == 6
 
-  , luaTestCase "isfunction" $ do
-      pushLuaExpr "function () print \"hi!\" end"
-      isfunction (-1)
+  , testGroup "Type checking"
+    [ luaTestCase "isfunction" $ do
+        pushLuaExpr "function () print \"hi!\" end"
+        isfunction (-1)
 
-  , luaTestCase "isnil" $ pushLuaExpr "nil" *> isnil (-1)
-  , luaTestCase "isnone" $ isnone 500 -- stack index 500 does not exist
-  , luaTestCase "isnoneornil" $ do
-      pushLuaExpr "nil"
-      (&&) <$> isnoneornil 500 <*> isnoneornil (-1)
+    , luaTestCase "isnil" $ pushLuaExpr "nil" *> isnil (-1)
 
+    , luaTestCase "isnone" $ isnone 500 -- stack index 500 does not exist
+
+    , luaTestCase "isnoneornil" $ do
+        pushLuaExpr "nil"
+        (&&) <$> isnoneornil 500 <*> isnoneornil (-1)
+    ]
+
+  , testCase "CFunction handling" . runLua $ do
+      pushcfunction LuaRaw.lua_open_debug_ptr
+      liftIO . assertBool "not recognized as CFunction" =<< iscfunction (-1)
+      liftIO . assertEqual "CFunction changed after receiving it from the stack"
+        LuaRaw.lua_open_debug_ptr =<< tocfunction (-1)
+
+  , testGroup "getting values"
+    [ testCase "tointegerx returns numbers verbatim" . runLua $ do
+        pushLuaExpr "149"
+        res <- tointegerx (-1)
+        liftIO $ assertEqual "Not the correct number" (Just 149) res
+
+    , testCase "tointegerx accepts strings coercible to integers" . runLua $ do
+        pushLuaExpr "'451'"
+        res <- tointegerx (-1)
+        liftIO $ assertEqual "Not the correct number" (Just 451) res
+
+    , testCase "tointegerx returns Nothing when given a boolean" . runLua $ do
+        pushLuaExpr "true"
+        liftIO . assertEqual "Not the correct number" Nothing =<< tointegerx (-1)
+
+    , testCase "tonumberx returns numbers verbatim" . runLua $ do
+        pushLuaExpr "14.9"
+        res <- tonumberx (-1)
+        liftIO $ assertEqual "Not the correct number" (Just 14.9) res
+
+    , testCase "tonumberx accepts strings as numbers" . runLua $ do
+        pushLuaExpr "'42.23'"
+        res <- tonumberx (-1)
+        liftIO $ assertEqual "Not the correct number" (Just 42.23) res
+
+    , testCase "tonumberx returns Nothing when given a boolean" . runLua $ do
+        pushLuaExpr "true"
+        liftIO . assertEqual "Not the correct number" Nothing =<< tonumberx (-1)
+    ]
+
   , luaTestCase "setting and getting a global works" $ do
       pushLuaExpr "{'Moin', Hello = 'World'}"
       setglobal "hamburg"
@@ -126,27 +167,63 @@
       assertBool "different lua threads are equal in haskell" (luaSt1 /= luaSt2)
 
   , testCase "thread status" . runLua $ do
-      status >>= liftIO . assertEqual "base status should be OK" LuaOK
+      status >>= liftIO . assertEqual "base status should be OK" OK
       openlibs
       getglobal' "coroutine.resume"
       pushLuaExpr "coroutine.create(function() coroutine.yield(9) end)"
       co <- tothread (-1)
       call 1 0
       liftIO . runLuaWith co $ do
-        liftIO . assertEqual "yielding will put thread status to Yield" LuaYield
+        liftIO . assertEqual "yielding will put thread status to Yield" Yield
           =<< status
 
-  , testCase "loadstring status" . runLua $ do
-      liftIO . assertEqual "loading a valid string doesn't return LuaOK"
-        LuaOK =<< loadstring "return 1"
-      liftIO . assertEqual "loading an invalid string doesn't return LuaErrSyntax"
-        LuaErrSyntax =<< loadstring "marzipan"
+  , testGroup "loading"
+    [ testCase "loadstring status" . runLua $ do
+        liftIO . assertEqual "loading a valid string doesn't return OK"
+          OK =<< loadstring "return 1"
+        liftIO . assertEqual "loading an invalid string doesn't return ErrSyntax"
+          ErrSyntax =<< loadstring "marzipan"
 
+    , testCase "dostring loading" . runLua $ do
+        liftIO . assertEqual "wrong dostring result"
+          ErrRun =<< (dostring "error 'this fails'")
+        liftIO . assertEqual "loading an invalid string doesn't return ErrSyntax"
+          ErrSyntax =<< dostring "marzipan"
+        liftIO . assertEqual "loading a valid program failed"
+          OK =<< dostring "return (2 + 3)"
+        liftIO . assertEqual "top of the stack should be result of last computation"
+          (5 :: LuaInteger) =<< peek (-1)
+
+    , testCase "loadfile loading" . runLua $ do
+        liftIO . assertEqual "wrong error code for non-existing file"
+          ErrFile =<< loadfile "./file-does-not-exist.lua"
+        liftIO . assertEqual "loading an invalid file doesn't return ErrSyntax"
+          ErrSyntax =<< loadfile "test/lua/syntax-error.lua"
+        liftIO . assertEqual "loading a valid program failed"
+          OK =<< loadfile "./test/lua/example.lua"
+        call 0 0
+        liftIO . assertEqual "fib function not defined or not correct"
+          (8 :: LuaInteger) =<< (getglobal "fib" *> pushinteger 6 *>
+                                 call 1 1 *> peek (-1))
+
+    , testCase "dofile loading" . runLua $ do
+        liftIO . assertEqual "wrong error code for non-existing file"
+          ErrFile =<< dofile "./file-does-not-exist.lua"
+        liftIO . assertEqual "loading an invalid file doesn't return ErrSyntax"
+          ErrSyntax =<< dofile "test/lua/syntax-error.lua"
+        liftIO . assertEqual "wrong dofile result"
+          ErrRun =<< dofile "test/lua/error.lua"
+        liftIO . assertEqual "loading a valid program failed"
+          OK =<< dofile "./test/lua/example.lua"
+        liftIO . assertEqual "fib function not defined or not correct"
+          (13 :: LuaInteger) =<< dostring "return fib(7)" *> peek (-1)
+    ]
+
   , testCase "pcall status" . runLua $ do
       liftIO . assertEqual "calling error did not lead to an error status"
-        LuaErrRun =<< (loadstring "error \"this fails\"" *> pcall 0 0 Nothing)
+        ErrRun =<< (loadstring "error \"this fails\"" *> pcall 0 0 Nothing)
       liftIO . assertEqual "calling error did not lead to an error status"
-        LuaErrErr =<< do
+        ErrErr =<< do
           pushLuaExpr "function () error 'error in error handler' end"
           loadstring "error 'this fails'" *> pcall 0 0 (Just (-2))
 
@@ -155,9 +232,9 @@
       forM_ [GCSTOP .. GCSETSTEPMUL] $ \what -> (gc what 23)
 
   , testGroup "compare"
-    [ testProperty "identifies strictly smaller values" $ compareWith (<) LuaLT
-    , testProperty "identifies smaller or equal values" $ compareWith (<=) LuaLE
-    , testProperty "identifies equal values" $ compareWith (==) LuaEQ
+    [ testProperty "identifies strictly smaller values" $ compareWith (<) Lua.LT
+    , testProperty "identifies smaller or equal values" $ compareWith (<=) Lua.LE
+    , testProperty "identifies equal values" $ compareWith (==) Lua.EQ
     ]
 
   , testProperty "lessthan works" $ \n1 n2 -> monadicIO $ do
@@ -168,12 +245,12 @@
       assert $ luaCmp == (n1 < n2)
 
   , testProperty "order of Lua types is consistent" $ \ lt1 lt2 ->
-      let n1 = fromLuaType (lt1 :: LTYPE)
-          n2 = fromLuaType lt2
+      let n1 = fromType lt1
+          n2 = fromType lt2
       in Prelude.compare n1 n2 == Prelude.compare lt1 lt2
   ]
 
-compareWith :: (Int -> Int -> Bool) -> LuaRelation -> Int -> Property
+compareWith :: (Int -> Int -> Bool) -> RelationalOperator -> Int -> Property
 compareWith op luaOp n = compareLT .&&. compareEQ .&&. compareGT
  where
   compareLT :: Property
@@ -200,5 +277,5 @@
       compare (-2) (-1) luaOp
     assert $ luaRes == op (n + 1) n
 
-instance Arbitrary LTYPE where
+instance Arbitrary Type where
   arbitrary = arbitraryBoundedEnum
diff --git a/test/Foreign/Lua/TypesTest.hs b/test/Foreign/Lua/TypesTest.hs
--- a/test/Foreign/Lua/TypesTest.hs
+++ b/test/Foreign/Lua/TypesTest.hs
@@ -74,6 +74,28 @@
 
     , testProperty "map of strings to LuaNumber"
       (prop_roundtripEqual :: Map String LuaNumber -> Property)
+
+    , testGroup "tuples"
+      [ testProperty "pair of LuaNumbers"
+        (prop_roundtripEqual :: (LuaNumber, LuaNumber) -> Property)
+      , testProperty "triple of LuaNumbers"
+        (prop_roundtripEqual :: (LuaNumber, LuaNumber, LuaNumber) -> Property)
+      , testProperty "quadruple of LuaNumbers"
+        (prop_roundtripEqual
+         :: (LuaNumber, LuaNumber, LuaNumber, LuaNumber) -> Property)
+      , testProperty "quintuple of LuaNumbers"
+        (prop_roundtripEqual
+         :: (LuaNumber, LuaNumber, LuaNumber, LuaNumber, LuaNumber) -> Property)
+      , testProperty "hextuple of Text, LuaNumbers and Booleans"
+        (prop_roundtripEqual
+         :: (Bool, LuaNumber, T.Text, Bool, LuaNumber, LuaNumber) -> Property)
+      , testProperty "septuple of Text, LuaNumber and Booleans"
+        (prop_roundtripEqual
+         :: (T.Text, Bool, LuaNumber, Bool, Bool, LuaNumber, Bool) -> Property)
+      , testProperty "octuple of Strings and Booleans"
+        (prop_roundtripEqual
+         :: (Bool, String, Bool, Bool, String, Bool, Bool, String) -> Property)
+      ]
     ]
 
   , testGroup "Random stack values"
diff --git a/test/Foreign/LuaTest.hs b/test/Foreign/LuaTest.hs
--- a/test/Foreign/LuaTest.hs
+++ b/test/Foreign/LuaTest.hs
@@ -58,7 +58,7 @@
       idx1 <- ref registryindex
       idx2 <- ref registryindex
       -- functions are removed from stack
-      liftIO . assert =<< fmap (TFUNCTION /=) (ltype (-1))
+      liftIO . assert =<< fmap (TypeFunction /=) (ltype (-1))
 
       -- get functions from registry
       rawgeti registryindex idx1
@@ -197,7 +197,7 @@
       (runLuaEither $ do
           openbase
           res <- loadstring luaCode
-          when (res == LuaOK) $ call 0 0)
+          when (res == OK) $ call 0 0)
     ]
   ]
 
diff --git a/test/lua/error.lua b/test/lua/error.lua
new file mode 100644
--- /dev/null
+++ b/test/lua/error.lua
@@ -0,0 +1,1 @@
+error 'running this program will cause an error'
diff --git a/test/lua/example.lua b/test/lua/example.lua
new file mode 100644
--- /dev/null
+++ b/test/lua/example.lua
@@ -0,0 +1,8 @@
+--- Compute the n-th fibonacci number.
+function fib(n)
+  local a, b = 0, 1
+  for i = 0, (n - 1) do
+    a, b = b, b + a
+  end
+  return a
+end
diff --git a/test/lua/syntax-error.lua b/test/lua/syntax-error.lua
new file mode 100644
--- /dev/null
+++ b/test/lua/syntax-error.lua
@@ -0,0 +1,1 @@
+just wrong
