Once you've finished reading the tutorial, you may be wondering about the relationship between Fennel and Lua. If you have never programmed in Lua before, don't fear! It is one of the simplest programming languages ever. It's possible to learn Fennel without writing any Lua code, but for certain concepts there's no substitute for the Lua documentation.
The book Programming in Lua is a great introduction. The first edition is available for free online and is still relevant. However, if you have programmed before in other languages, you might benefit by focusing on specific areas where Lua is substantially different.
Lua's types include:
Of these, tables are by far the most complex as well as being the most
different from what you may be used to in other languages. The most
important consideration is that tables are used for both sequential
data (aka lists, vectors, or arrays) as well as associative data (aka
maps, dictionaries, or hashes). The same table can be used in both
roles; whether a table is sequential or associative is not an inherent
property of the table itself but determined by how a given piece of code
interacts with the table. Iterating over a table with
treat it as an array, while
pairs will treat it as an unordered
The Lua reference manual covers the entire language (including details of newer versions) in a more terse form which you may find more convenient when looking for specific things. The rest of this document provides a very brief overview of the standard library.
Other Lua runtimes or embedded contexts usually introduce things that aren't covered here.
tonumber: converts its string argument to a number; takes optional base
tostring: converts its argument to a string
tostringof all its arguments separated by tab characters
type: returns a string describing the type of its argument
pcall: calls a function in protected mode so errors are not fatal
error: halts execution and break to the nearest
assert: raises an error if a condition is nil/false, otherwise returns it
ipairs: iterates over sequential tables
pairs: iterates over any table, sequential or not, in undefined order
unpack: turns a sequential table into multiple values (table.unpack in 5.2+)
require: loads and returns a given module
tostring on tables will give unsatisfactory results; simply
evaluating the table in the REPL will invoke
fennel.view for you, and
show a human-readable view of the table (or you can invoke
explicitly in your code).
This module contains functions for operating on the filesystem. Note that directory listing is absent; you need the luafilesystem library for that.
To open a file you use
io.open, which returns a file descriptor upon
success, or nil and a message upon failure. This failure behavior
makes it well-suited for wrapping with
assert to turn failure into
an error. You can call methods on the file descriptor, concluding with
(let [f (assert (io.open "path/to/file"))] (print (f:read)) ; reads a single line by default (print (f:read "*a")) ; you can read the whole file (f:close))
You can also call
:w as its second argument to open
the file in write mode and then call
f:flush on the
The other important function in this module is the
function, which returns an iterator over all the file's lines.
(each [line (io.lines "path/to/file")] (process-line line))
It will automatically close the file once it detects the end of the
file. You can also call
f:lines on a file descriptor that you got
This contains some basic table manipulation functions. All these functions operate on sequential tables, not general key/value tables. The most important ones are described below:
table.insert function takes a table, an optional position, and
an element, and it will insert the element into the table at that
position. The position defaults to being the end of the
table.remove takes a table and an optional
position, removes the element at that position, and returns it. The
position defaults to the last element in the table. To remove
something from a non-sequential table, simply set its key to nil.
table.concat function returns a string that has all the elements
concatenated together with an optional separator.
(let [t [1 2 3]] (table.insert t 2 "a") ; t is now [1 "a" 2 3] (table.insert t "last") ; now [1 "a" 2 3 "last"] (print (table.remove t)) ; prints "last" (table.remove t 1) ; t is now ["a" 2 3] (print (table.concat t ", "))) prints "a, 2, 3"
table.sort function sorts a table in-place, as a side-effect. It
takes an optional comparator function which should return true when
its first argument is less than the second.
table.unpack function returns all the elements in the table as
multiple values. Note that
table.unpack is just
unpack in Lua 5.1.
You can explore a module by evaluating it in the REPL to display all the functions and values it contains.
math: all your standard math functions, trig, pseudorandom generator, etc
string: all common string operations (except
splitwhich is absent)
os: operating system functions like
Note that Lua does not implement regular expressions but its own more
limited pattern language for
_G: a table of all globals
setfenv: access to first-class function environments in Lua 5.1; in 5.2 onward use the _ENV table instead
setmetatable: metatables allow you to override the behavior of tables in flexible ways with functions of your choice
coroutine: the coroutine module allows you to do flexible control transfer in a first-class way
package: this module tracks and controls the loading of modules
arg: table of command-line arguments passed to the process
...: arguments passed to the current function; acts as multiple values
select: most commonly used with
...to find the number of arguments
xpcall: acts like
pcallbut accepts a handler; used to get a full stack trace rather than a single line number for errors
... values also work at the top level of a file. They are
usually used to capture command-line arguments for files run directly
from the command line, but they can also pass on values from a
dofile call or tell you the name of the current module in a file
that's loaded from
require. Note that since
multiple values it is common to put it in a table to store it, unless
the number of values is known ahead of time:
(local (first-arg second-arg) ...) (local all-args [...])
These are used for loading Lua code. The
load* functions return a
"chunk" function which must be called before the code gets run, but
dofile executes immediately.
_VERSION: the current version of Lua being used as a string
collectgarbage: you hopefully will never need this
debug: see the Lua manual for this module
next: needed for implementing your own iterators
rawset: operations which bypass metatables