diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,16 @@
 
 `pandoc-lua-marshal` uses [PVP Versioning][].
 
+## 0.1.4
+
+Released 2022-01-29.
+
+-   Export AttributeList type and marshaling functions from
+    `Text.Pandoc.Marshal.Attr`, namely `typeAttributeList`,
+    `peekAttributeList`, and `pushAttributeList`.
+
+-   Update to hslua 2.1, making use of the new utility functions.
+
 ## 0.1.3.1
 
 Released 2022-01-14.
diff --git a/pandoc-lua-marshal.cabal b/pandoc-lua-marshal.cabal
--- a/pandoc-lua-marshal.cabal
+++ b/pandoc-lua-marshal.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.4
 name:                pandoc-lua-marshal
-version:             0.1.3.1
+version:             0.1.4
 synopsis:            Use pandoc types in Lua
 description:         This package provides functions to marshal and unmarshal
                      pandoc document types to and from Lua.
@@ -49,9 +49,9 @@
                      , bytestring            >= 0.10     && < 0.12
                      , containers            >= 0.6      && < 0.7
                      , exceptions            >= 0.8      && < 0.11
-                     , lua                   >= 2.0.2    && < 2.1
-                     , hslua                 >= 2.0.1    && < 2.1
-                     , hslua-marshalling     >= 2.0.1    && < 2.1
+                     , lua                   >= 2.1      && < 2.2
+                     , hslua                 >= 2.1      && < 2.2
+                     , hslua-marshalling     >= 2.1      && < 2.2
                      , pandoc-types          >= 1.22.1   && < 1.23
                      , safe                  >= 0.3      && < 0.4
                      , text                  >= 1.1.1.0  && < 1.3
diff --git a/src/Text/Pandoc/Lua/Marshal/Attr.hs b/src/Text/Pandoc/Lua/Marshal/Attr.hs
--- a/src/Text/Pandoc/Lua/Marshal/Attr.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Attr.hs
@@ -18,6 +18,9 @@
   ( typeAttr
   , peekAttr
   , pushAttr
+  , typeAttributeList
+  , pushAttributeList
+  , peekAttributeList
   , mkAttr
   , mkAttributeList
   ) where
@@ -54,8 +57,8 @@
       (pushPandocList pushText, \(_,classes,_) -> classes)
       (peekList peekText, \(ident,_,kv) -> (ident,,kv))
   , property "attributes" "various element attributes"
-      (pushAttribs, \(_,_,attribs) -> attribs)
-      (peekAttribs, \(ident,cls,_) -> (ident,cls,))
+      (pushAttributeList, \(_,_,attribs) -> attribs)
+      (peekAttributeList, \(ident,cls,_) -> (ident,cls,))
   , method $ defun "clone"
     ### return
     <#> parameter peekAttr "attr" "Attr" ""
@@ -72,8 +75,8 @@
 
 -- | Retrieves an associated list of attributes from a table or an
 -- @AttributeList@ userdata object.
-peekAttribs :: LuaError e => Peeker e [(Text,Text)]
-peekAttribs idx = liftLua (ltype idx) >>= \case
+peekAttributeList :: LuaError e => Peeker e [(Text,Text)]
+peekAttributeList idx = liftLua (ltype idx) >>= \case
   TypeUserdata -> peekUD typeAttributeList idx
   TypeTable    -> liftLua (rawlen idx) >>= \case
     0 -> peekKeyValuePairs peekText peekText idx
@@ -82,16 +85,16 @@
 
 -- | Pushes an associated list of attributes as @AttributeList@ userdata
 -- object.
-pushAttribs :: LuaError e => Pusher e [(Text, Text)]
-pushAttribs = pushUD typeAttributeList
+pushAttributeList :: LuaError e => Pusher e [(Text, Text)]
+pushAttributeList = pushUD typeAttributeList
 
 -- | Constructor functions for 'AttributeList' elements.
 typeAttributeList :: LuaError e => DocumentedType e [(Text, Text)]
 typeAttributeList = deftype "AttributeList"
   [ operation Eq $ lambda
-    ### liftPure2 (==)
-    <#> parameter peekAttribs "a1" "AttributeList" ""
-    <#> parameter peekAttribs "a2" "AttributeList" ""
+    ### liftPure2 (\a b -> Just True == ((==) <$> a <*> b))
+    <#> parameter (optional . peekAttributeList) "a" "any" ""
+    <#> parameter (optional . peekAttributeList) "b" "any" ""
     =#> functionResult pushBool "boolean" "whether the two are equal"
 
   , operation Index $ lambda
@@ -105,7 +108,7 @@
     ### setKey
     <#> udparam typeAttributeList "t" "attributes list"
     <#> parameter peekKey "string|integer" "key" "lookup key"
-    <#> optionalParameter peekAttribute "string|nil" "value" "new value"
+    <#> opt (parameter peekAttribute "string|nil" "value" "new value")
     =#> []
 
   , operation Len $ lambda
@@ -207,7 +210,7 @@
     then do
       ident <- peekIndexRaw 1 peekText idx
       classes <- fromMaybe [] <$!> optional (peekIndexRaw 2 peekClasses idx)
-      attribs <- fromMaybe [] <$!> optional (peekIndexRaw 3 peekAttribs idx)
+      attribs <- fromMaybe [] <$!> optional (peekIndexRaw 3 peekAttributeList idx)
       return $ ident `seq` classes `seq` attribs `seq`
         (ident, classes, attribs)
     else retrieving "HTML-like attributes" $ do
@@ -225,7 +228,7 @@
           TypeString -> forcePeek $ do
             mident <- optional (peekText (nthBottom 1))
             mclass <- optional (peekList peekText (nthBottom 2))
-            mattribs <- optional (peekAttribs (nthBottom 3))
+            mattribs <- optional (peekAttributeList (nthBottom 3))
             return ( fromMaybe "" mident
                    , fromMaybe [] mclass
                    , fromMaybe [] mattribs)
@@ -242,6 +245,7 @@
 mkAttributeList :: LuaError e => DocumentedFunction e
 mkAttributeList = defun "AttributeList"
   ### return
-  <#> parameter peekAttribs "table|AttributeList" "attribs" "an attribute list"
+  <#> parameter peekAttributeList "table|AttributeList" "attribs"
+        "an attribute list"
   =#> functionResult (pushUD typeAttributeList) "AttributeList"
         "new AttributeList object"
diff --git a/src/Text/Pandoc/Lua/Marshal/Block.hs b/src/Text/Pandoc/Lua/Marshal/Block.hs
--- a/src/Text/Pandoc/Lua/Marshal/Block.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Block.hs
@@ -213,8 +213,6 @@
     <#> parameter peekFilter "Filter" "lua_filter" "table of filter functions"
     =#> functionResult pushBlock "Block" "modified element"
   ]
- where
-  boolResult = functionResult pushBool "boolean"
 
 getBlockContent :: Block -> Possible Content
 getBlockContent = \case
@@ -350,8 +348,8 @@
                      let defListAttrib = (1, DefaultStyle, DefaultDelim)
                      in OrderedList (fromMaybe defListAttrib mListAttrib) items)
     <#> blockItemsParam "ordered list items"
-    <#> optionalParameter peekListAttributes "ListAttributes" "listAttributes"
-                          "specifier for the list's numbering"
+    <#> opt (parameter peekListAttributes "ListAttributes" "listAttributes"
+                       "specifier for the list's numbering")
     =#> blockResult "OrderedList element"
 
   , defun "Para"
@@ -367,7 +365,7 @@
   , defun "RawBlock"
     ### liftPure2 RawBlock
     <#> parameter peekFormat "Format" "format" "format of content"
-    <#> parameter peekText "string" "text" "raw content"
+    <#> textParam "text" "raw content"
     =#> blockResult "RawBlock element"
 
   , defun "Table"
@@ -392,9 +390,7 @@
   peekItemsFuzzy idx = peekList peekBlocksFuzzy idx
     <|> ((:[]) <$!> peekBlocksFuzzy idx)
 
-  textParam = parameter peekText "string"
-  optAttrParam = optionalParameter peekAttr "attr" "Attr"
-    "additional attributes"
+  optAttrParam = opt (parameter peekAttr "Attr" "attr" "additional attributes")
 
 
 -- | Constructor for a list of `Block` values.
diff --git a/src/Text/Pandoc/Lua/Marshal/Cell.hs b/src/Text/Pandoc/Lua/Marshal/Cell.hs
--- a/src/Text/Pandoc/Lua/Marshal/Cell.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Cell.hs
@@ -107,8 +107,8 @@
                   (maybe 1 ColSpan mColSpan)
                   blocks)
   <#> parameter peekBlocksFuzzy "Blocks" "blocks" "document contents"
-  <#> optionalParameter peekAlignment "integer" "align" "cell alignment"
-  <#> optionalParameter peekIntegral "integer" "row_span" "rows to span"
-  <#> optionalParameter peekIntegral "integer" "col_span" "columns to span"
-  <#> optionalParameter peekAttr "Attr" "attr" "cell attributes"
+  <#> opt (parameter peekAlignment "integer" "align" "cell alignment")
+  <#> opt (parameter peekIntegral "integer" "row_span" "rows to span")
+  <#> opt (parameter peekIntegral "integer" "col_span" "columns to span")
+  <#> opt (parameter peekAttr "Attr" "attr" "cell attributes")
   =#> functionResult pushCell "Cell" "new Cell object"
diff --git a/src/Text/Pandoc/Lua/Marshal/Citation.hs b/src/Text/Pandoc/Lua/Marshal/Citation.hs
--- a/src/Text/Pandoc/Lua/Marshal/Citation.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Citation.hs
@@ -86,11 +86,11 @@
            , citationNoteNum = fromMaybe 0 mnote_num
            , citationHash = fromMaybe 0 mhash
            })
-  <#> parameter peekText "string" "cid" "citation ID (e.g. bibtex key)"
+  <#> textParam "cid" "citation ID (e.g. bibtex key)"
   <#> parameter peekCitationMode "CitationMode" "mode" "citation rendering mode"
-  <#> optionalParameter peekInlinesFuzzy "prefix" "Inlines" ""
-  <#> optionalParameter peekInlinesFuzzy "suffix" "Inlines" ""
-  <#> optionalParameter peekIntegral "note_num" "integer" "note number"
-  <#> optionalParameter peekIntegral "hash" "integer" "hash number"
+  <#> opt (parameter peekInlinesFuzzy "prefix" "Inlines" "")
+  <#> opt (parameter peekInlinesFuzzy "suffix" "Inlines" "")
+  <#> opt (integralParam "note_num" "note number")
+  <#> opt (integralParam "hash"     "hash number")
   =#> functionResult pushCitation "Citation" "new citation object"
   #? "Creates a single citation."
diff --git a/src/Text/Pandoc/Lua/Marshal/Filter.hs b/src/Text/Pandoc/Lua/Marshal/Filter.hs
--- a/src/Text/Pandoc/Lua/Marshal/Filter.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Filter.hs
@@ -30,7 +30,7 @@
 
 import Prelude hiding (lookup)
 import Control.Applicative ((<|>), optional)
-import Control.Monad ((<$!>))
+import Control.Monad ((<$!>), void)
 import Data.Data
   ( Data, dataTypeConstrs, dataTypeName, dataTypeOf
   , showConstr, toConstr, tyconUQname )
@@ -50,7 +50,7 @@
 -- Filter functions are stored in the registry and retrieved from there.
 pushFilterFunction :: LuaError e => FilterFunction -> LuaE e ()
 pushFilterFunction (FilterFunction fnRef) =
-  getref registryindex fnRef
+  void $ getref registryindex fnRef
 
 -- | Retrieves a filter function from the stack.
 --
diff --git a/src/Text/Pandoc/Lua/Marshal/Inline.hs b/src/Text/Pandoc/Lua/Marshal/Inline.hs
--- a/src/Text/Pandoc/Lua/Marshal/Inline.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Inline.hs
@@ -291,8 +291,8 @@
     =#> functionResult pushInline "Inline" "cite element"
   , defun "Code"
     ### liftPure2 (\text mattr -> Code (fromMaybe nullAttr mattr) text)
-    <#> parameter peekText "code" "string" "code string"
-    <#> optionalParameter peekAttr "attr" "Attr" "additional attributes"
+    <#> textParam "code" "code string"
+    <#> opt (parameter peekAttr "Attr" "attr" "additional attributes")
     =#> functionResult pushInline "Inline" "code element"
   , mkInlinesConstr "Emph" Emph
   , defun "Image"
@@ -301,9 +301,9 @@
                          title = fromMaybe mempty mtitle
                      in Image attr caption (src, title))
     <#> parameter peekInlinesFuzzy "Inlines" "caption" "image caption / alt"
-    <#> parameter peekText "string" "src" "path/URL of the image file"
-    <#> optionalParameter peekText "string" "title" "brief image description"
-    <#> optionalParameter peekAttr "Attr" "attr" "image attributes"
+    <#> textParam "src" "path/URL of the image file"
+    <#> opt (textParam "title" "brief image description")
+    <#> opt (parameter peekAttr "Attr" "attr" "image attributes")
     =#> functionResult pushInline "Inline" "image element"
   , defun "LineBreak"
     ### return LineBreak
@@ -314,14 +314,14 @@
                          title = fromMaybe mempty mtitle
                      in Link attr content (target, title))
     <#> parameter peekInlinesFuzzy "Inlines" "content" "text for this link"
-    <#> parameter peekText "string" "target" "the link target"
-    <#> optionalParameter peekText "string" "title" "brief link description"
-    <#> optionalParameter peekAttr "Attr" "attr" "link attributes"
+    <#> textParam "target" "the link target"
+    <#> opt (textParam "title" "brief link description")
+    <#> opt (parameter peekAttr "Attr" "attr" "link attributes")
     =#> functionResult pushInline "Inline" "link element"
   , defun "Math"
     ### liftPure2 Math
     <#> parameter peekMathType "quotetype" "Math" "rendering method"
-    <#> parameter peekText "text" "string" "math content"
+    <#> textParam "text" "math content"
     =#> functionResult pushInline "Inline" "math element"
   , defun "Note"
     ### liftPure Note
@@ -335,7 +335,7 @@
   , defun "RawInline"
     ### liftPure2 RawInline
     <#> parameter peekFormat "format" "Format" "format of content"
-    <#> parameter peekText "text" "string" "string content"
+    <#> textParam "text" "string content"
     =#> functionResult pushInline "Inline" "raw inline element"
   , mkInlinesConstr "SmallCaps" SmallCaps
   , defun "SoftBreak"
@@ -347,11 +347,11 @@
   , defun "Span"
     ### liftPure2 (\inlns mattr -> Span (fromMaybe nullAttr mattr) inlns)
     <#> parameter peekInlinesFuzzy "content" "Inlines" "inline content"
-    <#> optionalParameter peekAttr "attr" "Attr" "additional attributes"
+    <#> opt (parameter peekAttr "Attr" "attr" "additional attributes")
     =#> functionResult pushInline "Inline" "span element"
   , defun "Str"
     ### liftPure Str
-    <#> parameter peekText "text" "string" ""
+    <#> textParam "text" ""
     =#> functionResult pushInline "Inline" "new Str object"
   , mkInlinesConstr "Strong" Strong
   , mkInlinesConstr "Strikeout" Strikeout
diff --git a/src/Text/Pandoc/Lua/Marshal/ListAttributes.hs b/src/Text/Pandoc/Lua/Marshal/ListAttributes.hs
--- a/src/Text/Pandoc/Lua/Marshal/ListAttributes.hs
+++ b/src/Text/Pandoc/Lua/Marshal/ListAttributes.hs
@@ -70,10 +70,10 @@
                    , fromMaybe DefaultStyle mstyle
                    , fromMaybe DefaultDelim mdelim
                    ))
-  <#> optionalParameter peekIntegral "integer" "start" "number of first item"
-  <#> optionalParameter peekRead "string" "style" "list numbering style"
-  <#> optionalParameter peekRead "string" "delimiter" "list number delimiter"
-  =#> functionResult pushListAttributes "ListAttributes" "new ListAttributes"
+  <#> opt (integralParam "start" "number of first item")
+  <#> opt (parameter peekRead "string" "style" "list numbering style")
+  <#> opt (parameter peekRead "string" "delimiter" "list number delimiter")
+  =#> udresult typeListAttributes "new ListAttributes"
   #? "Creates a new ListAttributes object."
 
 -- | Pushes a 'ListNumberDelim' value as string.
diff --git a/src/Text/Pandoc/Lua/Marshal/MetaValue.hs b/src/Text/Pandoc/Lua/Marshal/MetaValue.hs
--- a/src/Text/Pandoc/Lua/Marshal/MetaValue.hs
+++ b/src/Text/Pandoc/Lua/Marshal/MetaValue.hs
@@ -86,7 +86,7 @@
 
   , defun "MetaBool"
     ### liftPure MetaBool
-    <#> parameter peekBool "boolean" "bool" "true or false"
+    <#> boolParam "bool" "true or false"
     =#> functionResult pushMetaValue "boolean" "input, unchanged"
 
   , defun "MetaInlines"
@@ -108,6 +108,6 @@
 
   , defun "MetaString"
     ### liftPure MetaString
-    <#> parameter peekText "string" "s" "string value"
+    <#> textParam "s" "string value"
     =#> functionResult pushMetaValue "string" "unchanged input"
   ]
diff --git a/src/Text/Pandoc/Lua/Marshal/Pandoc.hs b/src/Text/Pandoc/Lua/Marshal/Pandoc.hs
--- a/src/Text/Pandoc/Lua/Marshal/Pandoc.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Pandoc.hs
@@ -90,7 +90,7 @@
 mkPandoc = defun "Pandoc"
   ### liftPure2 (\blocks mMeta -> Pandoc (fromMaybe nullMeta mMeta) blocks)
   <#> parameter peekBlocksFuzzy "Blocks" "blocks" "document contents"
-  <#> optionalParameter peekMeta "Meta" "meta" "document metadata"
+  <#> opt (parameter peekMeta "Meta" "meta" "document metadata")
   =#> functionResult pushPandoc "Pandoc" "new Pandoc document"
 
 -- | Constructor for 'Meta' values.
diff --git a/src/Text/Pandoc/Lua/Marshal/Row.hs b/src/Text/Pandoc/Lua/Marshal/Row.hs
--- a/src/Text/Pandoc/Lua/Marshal/Row.hs
+++ b/src/Text/Pandoc/Lua/Marshal/Row.hs
@@ -88,7 +88,6 @@
   ### liftPure2 (\mCells mAttr -> Row
                   (fromMaybe nullAttr mAttr)
                   (fromMaybe [] mCells))
-  <#> optionalParameter (peekList peekCellFuzzy) "{Cell,...}" "cells"
-        "row cells"
-  <#> optionalParameter peekAttr "Attr" "attr" "cell attributes"
+  <#> opt (parameter (peekList peekCellFuzzy) "{Cell,...}" "cells" "row cells")
+  <#> opt (parameter peekAttr "Attr" "attr" "cell attributes")
   =#> functionResult pushRow "Row" "new Row object"
diff --git a/src/Text/Pandoc/Lua/Marshal/TableFoot.hs b/src/Text/Pandoc/Lua/Marshal/TableFoot.hs
--- a/src/Text/Pandoc/Lua/Marshal/TableFoot.hs
+++ b/src/Text/Pandoc/Lua/Marshal/TableFoot.hs
@@ -67,7 +67,6 @@
   ### liftPure2 (\mCells mAttr -> TableFoot
                   (fromMaybe nullAttr mAttr)
                   (fromMaybe [] mCells))
-  <#> optionalParameter (peekList peekRowFuzzy) "{Row,...}" "rows"
-        "footer rows"
-  <#> optionalParameter peekAttr "Attr" "attr" "table foot attributes"
+  <#> opt (parameter (peekList peekRowFuzzy) "{Row,...}" "rows" "footer rows")
+  <#> opt (parameter peekAttr "Attr" "attr" "table foot attributes")
   =#> functionResult pushTableFoot "TableFoot" "new TableFoot object"
diff --git a/src/Text/Pandoc/Lua/Marshal/TableHead.hs b/src/Text/Pandoc/Lua/Marshal/TableHead.hs
--- a/src/Text/Pandoc/Lua/Marshal/TableHead.hs
+++ b/src/Text/Pandoc/Lua/Marshal/TableHead.hs
@@ -67,7 +67,6 @@
   ### liftPure2 (\mRows mAttr -> TableHead
                   (fromMaybe nullAttr mAttr)
                   (fromMaybe [] mRows))
-  <#> optionalParameter (peekList peekRowFuzzy) "{Row,...}" "rows"
-        "header rows"
-  <#> optionalParameter peekAttr "Attr" "attr" "table head attributes"
+  <#> opt (parameter (peekList peekRowFuzzy) "{Row,...}" "rows" "header rows")
+  <#> opt (parameter peekAttr "Attr" "attr" "table head attributes")
   =#> functionResult pushTableHead "TableHead" "new TableHead object"
diff --git a/src/Text/Pandoc/Lua/Walk.hs b/src/Text/Pandoc/Lua/Walk.hs
--- a/src/Text/Pandoc/Lua/Walk.hs
+++ b/src/Text/Pandoc/Lua/Walk.hs
@@ -27,7 +27,7 @@
 
 import Prelude hiding (lookup)
 import Control.Applicative ((<|>))
-import Control.Monad ((<$!>), when)
+import Control.Monad ((<$!>))
 import Data.Data (Data)
 import Data.Proxy (Proxy (..))
 import HsLua
@@ -81,7 +81,7 @@
 applyStraightFunction fn pushElement peekElement x = do
   pushFilterFunction fn
   pushElement x
-  callWithTraceback 1 2
+  callTrace 1 2
   forcePeek . (`lastly` pop 2) $
     (,)
     <$> ((x <$ peekNil (nth 2)) <|> peekElement (nth 2))
@@ -137,7 +137,7 @@
 applySplicingFunction fn pushElement peekElements x = do
   pushFilterFunction fn
   pushElement x
-  callWithTraceback 1 2
+  callTrace 1 2
   forcePeek . (`lastly` pop 2) $
     (,)
     <$> (liftLua (ltype (nth 2)) >>= \case
@@ -148,24 +148,6 @@
 --
 -- Helper
 --
-
--- | Like @'Lua.call'@, but adds a traceback to the error message (if any).
-callWithTraceback :: forall e. LuaError e
-                  => NumArgs -> NumResults -> LuaE e ()
-callWithTraceback nargs nresults = do
-  let traceback' :: LuaE e NumResults
-      traceback' = do
-        l <- state
-        msg <- tostring' (nthBottom 1)
-        traceback l (Just msg) 2
-        return 1
-  tracebackIdx <- absindex (nth (fromNumArgs nargs + 1))
-  pushHaskellFunction traceback'
-  insert tracebackIdx
-  result <- pcall nargs nresults (Just tracebackIdx)
-  remove tracebackIdx
-  when (result /= OK)
-    throwErrorAsException
 
 data TraversalControl = Continue | Stop
 
