Module functional

A module for functional programming utils.

About the module

This module seeks to provide some utility functions and structures which are too verbose in vanilla lua, in particular with regards to iteration and inline function definition.

The module is writen completely in vanilla lua, with no dependencies on external packages. This was a decision made for portability, and has drawbacks. Since none of this was written as a C binding, it is not as performant as it could be.

For example, luafun is "high-performance functional programming library for Lua designed with LuaJIT's trace compiler in mind" . If your environment allows you to use LuaJIT and performance is a concern, perhaps luafun will be more suited for your needs.

The motivation behind this module is, again, portability. If you want to embed this code on a webpage, or use it in some weird system for which a C binding wouldn't work, this project is aimed at you.

Definitions

Array

As lua doesn't have a dedicated array type, the word "array" in this document referes to a table with contiguous non-nil values starting at index 1.

Iterable

An iterable refers to either of:

  • An array (see above); or
  • An instance of Iterator.

Info:

  • Copyright: 2021
  • Release: 1.6.0
  • License: MIT
  • Author: William Quelho Ferreira

Functions

iterate (iterable) Create an Iterator for the iterable.
counter () Iterate over the naturals starting at 1.
range ([start=1], stop[, step=1]) Create an integer iterator that goes from start to stop, step-wise.
filter (iterable, predicate) Select only values which match the predicate.
map (iterable, mapping) Map values into new values.
reduce (iterable, reducer, initial_value) Collapse values into a single value.
foreach (iterable, func) Apply a function to all values.
last (iterable) Return the last value of the given iterable.
take (iterable, n) Iterate over the n first values and stop.
skip (iterable, n) Iterate over the values, starting at the (n+1)th one.
every (iterable, n) Take 1 value every n.
any (iterable, predicate) Checks if any values evaluate to true.
all (iterable, predicate) Checks if all values evaluate to true.
zip () Iterate over two iterables simultaneously.
packed_zip () Iterate over two iterables simultaneously.
enumerate () Iterate with an added index.
concat () Concatenate two iterables into an Iterator.
nop () Does nothing.
identity (...) Returns its arguments in the same order.
lambda (lambda_def[, env={}]) Create a lambda function from a given definition string.
clambda (expr, extra_env) Create a context-aware lambda function.
to_array (iterable) Return an array version of the iterable.
to_coroutine (iterable) Create a coroutine that yields the values.
negate (predicate) Create a negated function of predicate.
compose (f1, f2, ...) Create a function composition from the given functions.
bind (func, ...) Create a function with bound arguments.
curry (func, levels) Curries a function by the given amount of arguments.
dict (t) Create a function that accesses t.
indexer (k) Create a function that accesses the key k for any table.
bind_self (t, k, ...) Create a bound function whose first argument is t.
constant (value) Create a function that always returns the same value.
import ([env=_G]) Import Iterator, lambda, and commonly used functions into a given scope.

Fields

_VERSION Module version.

Class Iterator

Iterator.over (iterable) Iterate over the given iterable.
Iterator:next () Retrieve the next element from the iterator, if any.
Iterator.counter () Iterate over the naturals starting at 1.
Iterator.range ([start=1], stop[, step=1]) Create an integer iterator that goes from start to stop, step-wise.
Iterator.from (func, is, var) Iterate over the function's returned values upon repeated calls.
Iterator.packed_from (func, is, var) Iterate over the function's returned values (packed into a table) upon repeated calls.
Iterator.from_coroutine (co) Iterate over the coroutine's yielded values.
Iterator.clone (iterable) Nondestructively return an independent iterable from the given one.
Iterator:filter (predicate) Select only values which match the predicate.
Iterator:map (mapping) Map values into new values.
Iterator:reduce (reducer, initial_value) Collapse values into a single value.
Iterator:foreach (func) Apply a function to all values.
Iterator:last () Consume the iterator and retrieve the last value it produces.
Iterator:take (n) Iterate over the n first values and stop.
Iterator:take_while (predicate) Iterate while predicate is true and stop.
Iterator:take_last (n) Consume the iterator and produce its n last values in order.
Iterator:take_until (predicate) Iterate while predicate is false and stop.
Iterator:skip (n) Iterate over the values, starting at the (n+1)th one.
Iterator:skip_while (predicate) Iterate over the values, starting whenever predicate becomes false for the first time.
Iterator:skip_until (predicate) Iterate over the values, starting whenever predicate becomes true for the first time.
Iterator:every (n) Take 1 value every n.
Iterator:any (predicate) Checks if any values evaluate to true.
Iterator:all (predicate) Checks if all values evaluate to true.
Iterator:count (predicate) Counts how many values evaluate to true.
Iterator:zip (other) Iterate over two iterables simultaneously.
Iterator:packed_zip (other) Iterate over two iterables simultaneously, giving their values as an array.
Iterator:enumerate () Include an index while iterating.
Iterator:concat (other) Append elements from other after this iterator has been exhausted.
Iterator:to_array () Create an array out of the Iterator's values.
Iterator:to_coroutine () Create a coroutine that yields the values.
Iterator:is_complete () Check whether or not the iterator is done.


Functions

iterate (iterable)
Create an Iterator for the iterable.

Equivalent to

Iterator.over(iterable)
.

Parameters:

  • iterable iterable the values to be iterated over

Returns:

    Iterator the new Iterator
counter ()
Iterate over the naturals starting at 1.

Returns:

    Iterator the counter

See also:

range ([start=1], stop[, step=1])
Create an integer iterator that goes from start to stop, step-wise.

Parameters:

  • start integer the start of the integer range (default 1)
  • stop integer the end of the integer range (inclusive)
  • step integer the difference between consecutive elements (default 1)

Returns:

    Iterator the new Iterator

See also:

filter (iterable, predicate)
Select only values which match the predicate.

Equivalent to

iterate(iterable):filter(predicate)
.

Parameters:

  • iterable iterable the values to be filtered
  • predicate predicate the function to evaluate for each value

Returns:

    Iterator the filtering Iterator

See also:

map (iterable, mapping)
Map values into new values.

Equivalent to

iterate(iterable):map(mapping)
.

Please note that at no point during iteration may the mapping function return nil as its first value.

Parameters:

  • iterable iterable the values to be mapped
  • mapping function the function to evaluate for each value

Returns:

    Iterator the mapping Iterator

See also:

reduce (iterable, reducer, initial_value)
Collapse values into a single value.

Equivalent to

iterate(iterable):reduce(reducer, initial_value)
.

A reducer is a function of the form

function(accumulated_value, new_value)
which returns the reducing or "accumulation" of accumulated_value and new_value

The definition of "reducing" is flexible, and a few common examples include sum and concatenation.

Parameters:

  • iterable iterable the values to be collapsed
  • reducer reducer the collapsing function
  • initial_value the initial value passed to the reducer

Returns:

    the accumulation of all values

See also:

foreach (iterable, func)
Apply a function to all values.

Equivalent to

iterate(iterable):foreach(func)
.

The main difference between foreach and map is that foreach ignores the return value(s) of its function, while map uses them and has restrictions on what it can return.

Another important difference is that map is a lazy evaluator, while foreach iterates over its values immediately.

Parameters:

  • iterable iterable the values to be iterated over
  • func function the function to apply for each value

See also:

last (iterable)
Return the last value of the given iterable.

If iterable is an iterator, this call is equivalent to iterable:last(). Otherwise, this call accesses the last element in the array.

Parameters:

  • iterable iterable the sequence whose last element is to be accessed

Returns:

    the last element in the sequence

See also:

take (iterable, n)
Iterate over the n first values and stop.

Equivalent to

iterate(iterable):take(n)
.

Parameters:

  • iterable iterable the values to be iterated over
  • n integer amount of values to take

Returns:

    Iterator the new Iterator

See also:

skip (iterable, n)
Iterate over the values, starting at the (n+1)th one.

Equivalent to

iterate(iterable):skip(n)
.

Parameters:

  • iterable iterable the values to be iterated over
  • n integer amount of values to skip

Returns:

    Iterator the new Iterator

See also:

every (iterable, n)
Take 1 value every n.

Equivalent to

iterate(iterable):every(n)
.

The first value is always taken.

Parameters:

  • iterable iterable the values to be iterated over
  • n integer one more than the number of skipped values

Returns:

    Iterator the new Iterator

See also:

any (iterable, predicate)
Checks if any values evaluate to true.

Equivalent to

iterate(iterable):any(predicate)
.

Parameters:

  • iterable iterable the values to be iterated over
  • predicate predicate the function to evaluate for each value, defaults to
    not (value == nil or value == false)

Returns:

    boolean true if and only if at least one of the values evaluate to true

See also:

all (iterable, predicate)
Checks if all values evaluate to true.

Equivalent to

iterate(iterable):all(predicate)
.

Parameters:

  • iterable iterable the values to be iterated over
  • predicate predicate the function to evaluate for each value, defaults to
    not (value == nil or value == false)

Returns:

    boolean true if and only if all of the values evaluate to true

See also:

zip ()
Iterate over two iterables simultaneously.

See also:

packed_zip ()
Iterate over two iterables simultaneously.

See also:

enumerate ()
Iterate with an added index.

See also:

concat ()
Concatenate two iterables into an Iterator.

See also:

nop ()
Does nothing.
identity (...)
Returns its arguments in the same order.

Parameters:

  • ... the values to be returned

Returns:

    the given values
lambda (lambda_def[, env={}])
Create a lambda function from a given definition string.

DO NOT USE THIS WITH UNTRUSTED AND/OR UNKNOWN STRINGS!

This and clambda are meant to facilitate the creation of inline functions, given the vanilla Lua syntax is quite verbose (especially with higher order functions).

To attempt limit user error and naïve attacks, some restrictions are put in place with respect to the definition:

  • It must be of the form (arglist) => body, where body is either another lambda definition or an expression that could normally be put inside parenthesis in Lua.
  • The ()=> chain must not be longer than 10 steps.
  • The environment env must not contain more than 150 string keys.
  • The definition string must not be longer than 100 bytes.
  • The definition string must not contain a newline ("\n") character.
  • The definition string must not contain the substring "--".
  • The definition string must not contain the word "end".

Parameters:

  • lambda_def string or table the definition to be made into a function
  • env table the function environment (default {})

Returns:

    function the generated function

See also:

Usage:

    local add = f.lambda "(x, y) => x + y"
    print(add(2, 3)) --> 5
    local emphasis = f.lambda("(x)=>x..suffix", {suffix = "!!"})
    -- f.lambda can even create higher order functions easily
    local linear_combinator = f.lambda "(m) => (x, y) => m*x + y"
    local comb2 = linear_combinator(2)
    print(comb2(3, 4)) --> 10
    -- lambdas can "see" variables given in their environment
    local emphasis = f.lambda("(x)=>x..suffix", {suffix = "!!"})
    emphasis = f.lambda{"(x)=>x..suffix", suffix = "!!"} -- alternative way to define environment
    print(emphasis("Hello")) --> Hello!!
clambda (expr, extra_env)
Create a context-aware lambda function.

DO NOT USE THIS WITH UNTRUSTED AND/OR UNKNOWN STRINGS!

This works similarly to lambda, except it automatically adds any non-false and non-nil locals and globals into its environment. As a flip-side, its environment is now read-only.

See lambda for more information on restrictions and usage.

Parameters:

  • expr string or table the expression to be made into a function
  • extra_env table additional values to insert into the environment

See also:

Usage:

    local angle = math.pi / 3
    local offset_sin = f.clambda "(x) => math.sin(x + angle)"
    print(offset_sin(math.pi)) --> -0.866
    print(offset_sin(2 * math.pi / 3)) --> 0
    local alternative = f.clambda("(x)=>sin(x + angle)", {sin=math.sin})
    local or_even = f.clambda{"(x)=>sin(x+angle)", sin=math.sin}
to_array (iterable)
Return an array version of the iterable.

If iterable is an array, return itself.

If iterable is an Iterator, return

iterable:to_array()

Parameters:

  • iterable iterable the values to make an array out of

Returns:

    array the array

See also:

to_coroutine (iterable)
Create a coroutine that yields the values. of the iterable.

Equivalent to

iterate(iterable):to_coroutine()
.

Parameters:

  • iterable iterable the values to be iterated over

Returns:

    thread The new coroutine

See also:

negate (predicate)
Create a negated function of predicate.

Parameters:

  • predicate predicate the function to be negated

Returns:

    predicate the inverted predicate
compose (f1, f2, ...)
Create a function composition from the given functions.

Parameters:

  • f1 function the outermost function of the composition
  • f2 function the second outermost function of the composition
  • ... function... any further functions to add to the composition, in order

Returns:

    function the composite function
bind (func, ...)
Create a function with bound arguments.

The bound function returned will call func with the arguments passed on to its creation.

If more arguments are given during its call, they are appended to the original ones.

Parameters:

  • func function the function to create a binding of
  • ... the arguments to bind to the function.

Returns:

    function the bound function
curry (func, levels)
Curries a function by the given amount of arguments.

Currying is, in simple terms, transforming a function f which is used as:

res = f(x, y, z)

Into a function g which is used as:

h = g(x); i = h(y); res = i(z)

Or, chaining all the calls into a single statement:

res = g(x)(y)(z)

You can read more about currying in the Wikipedia page on currying

In all levels except the deepest, any arguments past the first are ignored. In the deepest level, though, they are passed along to the function as normal. For an example, see Usage.

Parameters:

  • func function the function to be curried
  • levels integer the number of levels to curry for

Returns:

    function the curried function

Usage:

    local function func(a, b, c, d) return a * b * c * d end
    local cfunc = f.curry(func, 3) -- 3 levels deep curry
    local c1 = cfunc(1) -- binds a = 1; 2 levels left after the call
    local c2 = c1(2, 3) -- binds b = 2, drops the 3; 1 level left after the call
    local res = c2(4, 5) -- calls w/ c = 4 & d = 5; this was the last level</pre>
dict (t)
Create a function that accesses t.

Creates a function that reads from t. The new function's argument is used as the key to index t.

Parameters:

  • t table the table to be indexed

Returns:

    function the dictionary function
indexer (k)
Create a function that accesses the key k for any table.

The argument passed to the returned function is used as the table t to be accessed. The value of t[k] is returned.

Parameters:

  • k the key to access

Returns:

    function the table indexer
bind_self (t, k, ...)
Create a bound function whose first argument is t.

Particularly useful to pass a method as a function.

Equivalent to

bind(t[k], t, ...)
.

Parameters:

  • t table the table to be accessed
  • k the key to be accessed
  • ... further arguments to bind to the function

Returns:

    function the binding for t[k]
constant (value)
Create a function that always returns the same value.

Parameters:

  • value the constant to be returned

Returns:

    function the constant function
import ([env=_G])
Import Iterator, lambda, and commonly used functions into a given scope.

Upon calling this, the following module entries will be added to the given environment. If env is nil, _G (global scope) is assumed.

They can still be accessed as usual through the module after the call.

Parameters:

  • env table the environment to import into (default _G)

Fields

_VERSION
Module version.

Class Iterator

A lazy-loading Iterator.
Iterator.over (iterable)
Iterate over the given iterable.

If iterable is an array, create an Iterator instance that returns its values one by one. If it is an iterator, return itself.

Parameters:

  • iterable iterable the values to be iterated over

Returns:

    Iterator the new Iterator
Iterator:next ()
Retrieve the next element from the iterator, if any.

Returns:

    the next value in the sequence, or nil if there is none
Iterator.counter ()
Iterate over the naturals starting at 1.

Returns:

    Iterator the counter

See also:

Iterator.range ([start=1], stop[, step=1])
Create an integer iterator that goes from start to stop, step-wise.

Parameters:

  • start integer the start of the integer range (default 1)
  • stop integer the end of the integer range (inclusive)
  • step integer the difference between consecutive elements (default 1)

Returns:

    Iterator the new Iterator

See also:

Iterator.from (func, is, var)
Iterate over the function's returned values upon repeated calls.

This can effectively convert a vanilla-Lua iterator into a capital I Iterator. For example, Iterator.from(io.lines "my_file.txt") gives you a string iterator over the lines in a file.

In general, any expression you can use in a for loop, you can wrap into an Iterator.from to get the same sequence of values in an Iterator form. For more information on iterators, read chapter 7 of Programming in Lua.

Since any repeated function call without arguments can be used as a vanilla Lua iterator, they can also be used with Iterator.from. For an example, see Usage.

Parameters:

  • func function the function to call
  • is invariant state passed to func
  • var initial variable passed to func

Returns:

    Iterator the new Iterator

See also:

Usage:

    local i = 0
    local function my_counter()
      i = i + 1
      if i > 10 then return nil end
      return i
    end
    f.from(my_counter):foreach(print) -- prints 1 through 10 and stops
Iterator.packed_from (func, is, var)
Iterate over the function's returned values (packed into a table) upon repeated calls. This is similar to Iterator.from, but instead of the created Iterator generating multiple return values per call, it returns them all packed into an array.

Parameters:

  • func function the function to call
  • is invariant state passed to func
  • var initial variable passed to func

Returns:

    Iterator the new Iterator

See also:

Iterator.from_coroutine (co)
Iterate over the coroutine's yielded values.

For example, assume you have a coroutine that produces messages from clients. One easy way to consume them in a for loop would be as in Usage.

Parameters:

  • co thread the coroutine to iterate

Returns:

    Iterator the new Iterator

Usage:

    local function my_totally_real_message_queue()
      coroutine.yield("this is a message")
      coroutine.yield("hello!")
      coroutine.yield("almost done")
      coroutine.yield("bye bye")
    end
    
    local co = coroutine.create(my_totally_real_message_queue)
    for count, message in f.Iterator.from_coroutine(co):enumerate() do
      io.write(count, ": ", message, "\n")
    end
    io.write("end")
Iterator.clone (iterable)
Nondestructively return an independent iterable from the given one.

If iterable is an Iterator, clone it according to its subtype. If iterable is an array, then return itself.

Please note that coroutine and iterated function call iterators cannot be cloned.

Parameters:

  • iterable iterable the iterable to be cloned

Returns:

    iterable the clone
Iterator:filter (predicate)
Select only values which match the predicate.

Parameters:

  • predicate predicate the function to evaluate for each value

Returns:

    Iterator the filtering Iterator
Iterator:map (mapping)
Map values into new values.

Please note that at no point during iteration may the mapping function return nil as its first value.

Parameters:

  • mapping function the function to evaluate for each value

Returns:

    Iterator the mapping Iterator
Iterator:reduce (reducer, initial_value)
Collapse values into a single value.

A reducer is a function of the form

function(accumulated_value, new_value)
which returns the reducing or "accumulation" of accumulated_value and new_value

The definition of "reducing" is flexible, and a few common examples include sum and concatenation.

Parameters:

  • reducer reducer the collapsing function
  • initial_value the initial value passed to the reducer

Returns:

    the accumulation of all values
Iterator:foreach (func)
Apply a function to all values.

The main difference between Iterator:foreach and Iterator:map is that foreach ignores the return value(s) of its function, while map uses them and has restrictions on what it can return.

Another important difference is that Iterator:map is a lazy evaluator, while Iterator:foreach iterates over its values immediately.

Parameters:

  • func function the function to apply for each value
Iterator:last ()
Consume the iterator and retrieve the last value it produces.

Returns:

    the last value produced by the iterator, or nil if there was none.
Iterator:take (n)
Iterate over the n first values and stop.

Parameters:

  • n integer amount of values to take

Returns:

    Iterator the new Iterator
Iterator:take_while (predicate)
Iterate while predicate is true and stop.

Parameters:

  • predicate predicate the predicate to check against

Returns:

    Iterator the new Iterator
Iterator:take_last (n)
Consume the iterator and produce its n last values in order.

Parameters:

  • n integer the number of elements to capture

Returns:

    Iterator the new Iterator which will produce said values
Iterator:take_until (predicate)
Iterate while predicate is false and stop.

Parameters:

  • predicate predicate the predicate to check against

Returns:

    Iterator the new Iterator
Iterator:skip (n)
Iterate over the values, starting at the (n+1)th one.

Parameters:

  • n integer amount of values to skip

Returns:

    Iterator the new Iterator
Iterator:skip_while (predicate)
Iterate over the values, starting whenever predicate becomes false for the first time.

Parameters:

  • predicate predicate the predicate to check against

Returns:

    Iterator the new Iterator
Iterator:skip_until (predicate)
Iterate over the values, starting whenever predicate becomes true for the first time.

Parameters:

  • predicate predicate the predicate to check against

Returns:

    Iterator the new Iterator
Iterator:every (n)
Take 1 value every n.

The first value is always taken.

Parameters:

  • n integer one more than the number of skipped values

Returns:

    Iterator the new Iterator

See also:

Iterator:any (predicate)
Checks if any values evaluate to true.

Parameters:

  • predicate predicate the function to evaluate for each value, defaults to
    not (value == nil or value == false)

Returns:

    boolean true if and only if at least one of the values evaluate to true
Iterator:all (predicate)
Checks if all values evaluate to true.

Parameters:

  • predicate predicate the function to evaluate for each value, defaults to
    not (value == nil or value == false)

Returns:

    boolean true if and only if all of the values evaluate to true
Iterator:count (predicate)
Counts how many values evaluate to true.

Parameters:

  • predicate predicate function to evaluate for each value; if nil, then counts all values.

Returns:

    integer the number of values that match the predicate
Iterator:zip (other)
Iterate over two iterables simultaneously.

This results in an Iterator with multiple values per :next() call.

The new Iterator will be considered complete as soon as the one the method was called on (self) is completed, regardless of the status of other.

Parameters:

  • other iterable the other iterable to zip with this one

Returns:

    Iterator the resulting zipped Iterator
Iterator:packed_zip (other)
Iterate over two iterables simultaneously, giving their values as an array.

This results in an Iterator with a single value per :next() call.

Parameters:

  • other iterable the other iterable to zip with this one

Returns:

    Iterator the resulting zipped Iterator
Iterator:enumerate ()
Include an index while iterating.

The index is given as a single value to the left of all return values. Otherwise, the iterator is unchanged. This is similar to how ipairs iterates over an array, except it can be used with any iterator.

Returns:

    Iterator the resulting indexed Iterator

See also:

Usage:

    letters = f.every({"a", "b", "c", "d", "e"}, 2)
    for idx, letter in letters:enumerate() do
      print(idx, letter)
    end
Iterator:concat (other)
Append elements from other after this iterator has been exhausted.

Parameters:

  • other iterable the iterator whose elements will be appended

Returns:

    Iterator the concatenation
Iterator:to_array ()
Create an array out of the Iterator's values.

Returns:

    array the array of values
Iterator:to_coroutine ()
Create a coroutine that yields the values. of the Iterator.

Returns:

    thread The new coroutine
Iterator:is_complete ()
Check whether or not the iterator is done.

Please note that even if the iterator has reached its actual last value, it has no way of knowing it was the last. Therefore, this function will only return true once the iterator returns nil for the first time.

Returns:

    boolean true if the Iterator has iterated over all its values.
generated by LDoc 1.4.6 Last updated 2022-08-13 17:30:57