Lua Functional Libraries
December 6, 2017 ยท View on GitHub
Updated 2017-12-05
This is a quick introduction to the Lua Fun library and how to write code using it. If you're completely unfamiliar with functional programming, you might want to start by reading this.
Lua Fun
Intro
Lua Fun is a library that provides several functions useful for writing Lua code in a functional style, such as the common map and reduce. For example, using Lua Fun, you would find the sum of the numbers 1 to 5 like this:
sum_1_to_5 = reduce(operator.add, 0, range(5))
range(5) returns a table with the numbers 1 to 5, {1, 2, 3, 4, 5} (technically it returns an iterator for those values, but iterators will be explained later). operator.add is simply a functional version of the + operator, i.e. it takes two arguments and returns the addition of those two arguments. reduce then repeatedly calls operator.add with the return value of the last call and the current value being iterated over, with 0 as the initial value for the first call. reduce stops and returns the return value of the final call once it has iterated over all the items given to it. Lua Fun also has a few shortcut functions for common useages of reduce. For example, the earlier summing code can also be written as sum_1_to_5 = sum(range(5)). A list of those shortcut function can be found here.
A reference for the functions available in Lua Fun can be found here.
Tips
- Try not to use anonymous functions inside calls to
map,reduce, andfilterbecause Lua, unfortunately, doesn't have very concise anonymous function syntax. Named local functions will usually be easier to read. - If you find yourself writing a long chain of functions like
reduce(... map(... map(... filter(...)))), try to break it up into smaller units and write functions for those units. The functions can then simply call each other to compose the chain. You can look at this for an example.
Special Notes
Iterators
Lua Fun functions don't technically take or return tables, they return iterators instead, which are actually a set of 3 values. Details can be found here if you're interested. All you really need to know though, is that because of this detail:
-
Tables will automatically be handled when passed into Lua Fun functions but, keep in mind that if your table has non-contiguous numeric keys and key
1is defined, Lua Fun will assume the table is an array and will only iterate through keys 1 up to the value that would be returned by Lua's length operator (read this for more info on the length operator). In order for that to happen, you have to explicitly dopairs(table). e.g.map(func, pairs(table)). -
If you need to produce an actual table to interop with code not using Lua Fun, or for some other reason, you can use the
totableortomapfunctions. Documentation for those functions is available here. -
Iterators can be used in for ... in ... loops if you prefer that syntax to Lua Fun's
foreachfunction. For example, you can dofor i in range(5) do ...
Parameter Order
If you've not dealt much with functional programming before, you might have gotten used to having callback functions as the last parameter of functions. Keep in mind that pretty much all Lua Fun functions that take functions as parameters have it as the first parameter. The reason for this is that it's convention, and makes more sense for functions like map, reduce, and filter, particularly when chaining them together. In particular though, keep in mind that even foreach takes the function as the first parameter rather than the data.
Reduce
If you've used other languages with functional primitives before, you may be used to reduce starting by calling the given function with the first 2 items in the given list. Keep in mind that Lua Fun's reduce has an initial value as a parameter instead. You can replicate the above mentioned behaviour of reduce by doing reduce(func, head(list), tail(list)) if necessary, though this causes an error if list is empty. Alternatively, reduce(func, nth(1, list), tail(list)) returns nil when list is empty.
Partial
While Lua Fun doesn't have partial, the OAA project has an implemetation that can be found in game/scripts/vscripts/libraries/functional.lua (file on GitHub). Usage should be similar to partial in other languages. The first parameter is the function to curry, followed by the parameters to pass to the curried function.