Skip to content

pmarreck/functional_lua

 
 

Repository files navigation

Functional Programming with Lua

Copyright (C) 2002-2022 std.functional authors

License workflow status codecov.io

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.

New Usage: Single File Include

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.

Core Features

-- 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)

New Functional Features

The combined library also includes new functional programming features:

Immutable Lists

-- 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)

Pattern Matching

-- 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
})

Pipe and Chain Operators

-- 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()

Maybe Type

-- 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)

Nix Support

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 build

Legacy Installation

The simplest and best way to install std.functional is with LuaRocks. To install the latest release):

    luarocks install std.functional

To 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.rockspec

The 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.

Documentation

The latest release of these libraries is documented in LDoc. Pre-built HTML files are included in the release.

Test Suite

All tests are now combined into a single comprehensive test file:

lua test

This 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 test

Bug reports and code contributions

These 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:

  1. Follow existing code. There are a lot of useful patterns and avoided traps there.

  2. 3-character indentation using SPACES in Lua sources: It makes rogue TABs easier to see, and lines up nicely with 'if' and 'end' keywords.

  3. Simple strings are easiest to type using single-quote delimiters, saving double-quotes for where a string contains apostrophes.

  4. Save horizontal space by only using SPACEs where the parser requires them.

  5. Use vertical space to separate out compound statements to help the coverage reports discover untested lines.

About

Functional Programming with Lua

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Shell 49.4%
  • Lua 47.2%
  • Nix 2.6%
  • Makefile 0.8%