1 Introduction
This article discusses the following module that supports functional programming in Lua: luafun -- https://luafun.github.io/index.html
The luafun library enable us to chain functions together, feeding the result of the previous function into the following function. And, they each support lazy evaluation, which means that, when we process items in a collection, the chain of processing for one item completes before the chain of evaluation for the next item starts. In effect, we do not have to wait for all the items in the collection to be processed before completing the chain of evaluation. The examples given below and the order of the messages that they print out makes this clear.
2 luafun -- Lua Functional Library
The latest version of the script discussed below is here: luafun_example.zip
Here is a Lua script that uses several of the features of luafun:
#!/usr/bin/env lua
--
-- Various tests for module `fun`.
-- Tests for laziness.
-- Test for `fun.chain`, `fun.map`, `fun.enumerate`, `fun.each`,
-- `fun.reduce`, `fun.cycle`, `fun.take`, etc.
--
M = {}
local fun = require('fun')
function M.test01(count, quiet)
quiet = quiet == true or false
local verbose = true
local a = { 11, 22, 33, 44, }
local b = { "aa", "bb", "cc", }
local c = fun.range(1, 3)
local calc_fn = function(x, n)
local val
if type(x) == 'number' then
val = x * n
elseif type(x) == 'string' then
val = x .. '|' .. x
else
val = 'unknown'
end
return val
end
local make_map_fun = function(msg, n, func)
local inner_fn = function(x)
if not quiet then
print(msg, x)
end
val = calc_fn(x, n)
return val
end
return inner_fn
end
local f1 = make_map_fun('(f1) x:', 5)
local f2 = make_map_fun('(f2) x:', 10)
local f3 = make_map_fun('(f3) x:', 100)
local f4 = make_map_fun('(f4) x:', 1000)
local show = function(i, v)
if verbose then
print(string.format('result -- i: %d v: %s', i, v))
print('----')
end
end
local acc_fn = function(acc, val)
if type(val) == 'number' then
acc = acc + val
elseif type(val) == 'string' then
acc = acc + #val
end
return acc
end
fun.chain(a, b, c)
:cycle()
:take(count)
:map(f1)
:map(f2)
:map(f3)
:map(f4)
:enumerate()
:each(show)
end
function M.main()
quiet = false
if arg[1] == '-h' or arg[1] == '--help' then
print('usage: lua test21.lua [-q | --quiet] [n]')
os.exit()
elseif arg[1] == '-q' or arg[1] == '--quiet' then
quiet = true
table.remove(arg, 1)
end
local count = 1
if #arg == 1 then
count = tonumber(arg[1])
end
print('test 08')
print('-------')
M.test01(count, quiet)
end
if os.getenv('LUACLI') == nil then
return M
else
M.main()
end
Notes:
In order to run this script from the command line, use something like this:
$ LUACLI=1 lua test21.lua 10 $ LUACLI=1 lua test21.lua --quiet 10
Using an environment variable (in this case LUACLI) enables us to use this script from either the command line for from within another Lua module.
In order to run this test at the Lua interactive prompt, do this:
> module = require('test21') > module.test01(20) > module.test01(20, true)We can modify a snippet in the above code so that it accumulates a sum of some of the values that we iterate over. We us fun.reduce (alias fun.foldl) to do the accumulation:
local sum = fun.chain(a, b, c) :cycle() :take(count) :map(f1) :map(f2) :map(f3) :map(f4) :reduce(acc_fn, 0) print('sum:', sum)See functions task1 and task2 in the sample code for functions that do these two tasks.
Lazy evaluation -- After running this script, if you inspect that messages that are printed out, you will notice that functions f1, f2, f3, and f4 all complete for the first item in the collection, before processing is begun for the next item. That will enable us to process large collections and to incrementally obtain results for some items in the collection without waiting for all the items in the collection.