Copyright (C) 2002-2022 std.functional authors
This is a collection of Functional Programming libraries for Lua 5.1 (including LuaJIT), 5.2, 5.3 and 5.4. The libraries are copyright by their authors (see the AUTHORS file for details), and released under the MIT license (the same license as Lua itself). There is no warranty.
std.functional has no run-time prerequisites beyond a standard Lua system, though it will take advantage of stdlib, strict and typecheck if they are installed.
You can now use this library as a single file include. Simply use:
local F = require 'functional'This will give you access to all of the std.functional API through a single
F table. The operator and tuple modules are available as F.operator and
F.tuple.
-- Basic examples
local square = F.lambda('|x| x * x')
local numbers = {1, 2, 3, 4, 5}
local squared = F.map(square, ipairs, numbers)
print(table.concat(squared, ", ")) -- 1, 4, 9, 16, 25
-- Create an immutable tuple
local t = F.tuple(1, 2, 3)The combined library also includes new functional programming features:
-- Create an immutable list
local list = F.list(1, 2, 3, 4, 5)
-- Access elements
local first = F.head(list) -- 1
local rest = F.tail(list) -- list(2, 3, 4, 5)
-- Prepend an element (non-destructive)
local new_list = F.cons(0, list) -- list(0, 1, 2, 3, 4, 5)
-- Check if a list is empty
if not F.is_empty(list) then
-- Process list
end
-- Take and drop
local first_three = F.take(3, list) -- list(1, 2, 3)
local last_two = F.drop(3, list) -- list(4, 5)-- Basic value matching
local result = F.match(value, {
[1] = function() return "one" end,
[5] = function() return "five" end,
[F.otherwise] = function() return "other" end
})
-- Type-based matching
local description = F.match(value, {
[F.is_string] = function(x) return "String: " .. x end,
[F.is_number] = function(x) return "Number: " .. x end,
[F.is_table] = function(x) return "Table with " .. #x .. " items" end,
[F.otherwise] = function() return "Unknown type" end
})
-- Pattern matching with guards
local category = F.match(num, {
[F.when(function(x) return x < 0 end)] = function() return "negative" end,
[F.when(function(x) return x == 0 end)] = function() return "zero" end,
[F.when(function(x) return x > 0 end)] = function() return "positive" end
})-- Create a processing pipeline
local add1 = function(x) return x + 1 end
local double = function(x) return x * 2 end
local square = function(x) return x * x end
-- Create a pipeline of functions (functional style)
local pipeline = F.pipe(add1, double, square)
local result = pipeline(5) -- ((5 + 1) * 2)^2 = 144
-- Process a value through multiple transformations (function form)
local total = F.pipe_val(data,
transform1,
transform2,
transform3
)
-- Cleaner array syntax for chaining operations
local result = F.pipe_with(initial_value, {
operation1,
operation2,
operation3
})
-- Method chaining approach (closer to Elixir's |> or F#'s |> pipe operators)
local pipeline = F.chain(add1)
:add(double)
:add(square)
local fn = pipeline:build()
local result = fn(5) -- ((5 + 1) * 2)^2 = 144
-- Process a value directly through a method chain
local result = F.chain_val(5)
:apply(add1) -- 6
:apply(double) -- 12
:apply(square) -- 144
:value()-- Create Maybe values
local just_value = F.just(42)
local nothing = F.nothing()
-- Check type
if F.is_just(result) then
-- Process the value
local value = F.from_just(result)
end
-- Map over a Maybe (safely)
local doubled = F.maybe_map(function(x) return x * 2 end, maybe_value)
-- Chain operations that might fail
local root = F.pipe_val(F.just(value),
function(m) return F.maybe_bind(safe_division, m) end,
function(m) return F.maybe_bind(safe_square_root, m) end
)
-- Provide a default for Nothing
local safe_result = F.maybe_default(0, maybe_value)This repository includes a flake.nix file for easy setup with the Nix package manager:
# Enter a development shell with all dependencies
nix develop
# Build the package
nix buildThe simplest and best way to install std.functional is with LuaRocks. To install the latest release):
luarocks install std.functionalTo install current git master (for testing, before submitting a bug report for example):
luarocks install http://github.com/lua-stdlib/functional/raw/master/std.functional-git-1.rockspecThe best way to install without LuaRocks is to copy functional.lua into
a directory on your package search path. Compatibility modules under
lib/std/functional are also provided for callers that still use the original
std.functional, std.functional.operator, or std.functional.tuple module
names.
The latest release of these libraries is documented in LDoc. Pre-built HTML files are included in the release.
All tests are now combined into a single comprehensive test file:
lua testThis single test file includes:
- Core functionality tests from the original spec
- Immutable list tests
- Pattern matching tests
- Pipe operator tests
- Method chain tests
- Maybe type tests
- Compose order tests
Or through Nix:
nix develop -c lua testThese libraries are written and maintained by their users.
Please make bug reports and suggestions as GitHub Issues. Pull requests are especially appreciated.
But first, please check that your issue has not already been reported by someone else, and that it is not already fixed by master in preparation for the next release (see Installation section above for how to temporarily install master with LuaRocks).
There is no strict coding style, but please bear in mind the following points when proposing changes:
-
Follow existing code. There are a lot of useful patterns and avoided traps there.
-
3-character indentation using SPACES in Lua sources: It makes rogue TABs easier to see, and lines up nicely with 'if' and 'end' keywords.
-
Simple strings are easiest to type using single-quote delimiters, saving double-quotes for where a string contains apostrophes.
-
Save horizontal space by only using SPACEs where the parser requires them.
-
Use vertical space to separate out compound statements to help the coverage reports discover untested lines.