Function Arguments in the lulu
Library
Introduction
Many lulu
library methods take a function as one of their arguments.
For example, the lulu.Array
class has various search and filter methods that expect to be passed a predicate function that will pick out specific elements in an array.
If a
is an Array
, we can use a:take_if
to extract elements that satisfy some predicate.
local a = Array{1,0,2,0,0,3}
local non_zero = a:take_if(function(x) return x > 0 end)
("%s has non-zero elements %s", a, non_zero) putln
This will output [1, 0, 2, 0, 0, 3] has non-zero elements [1, 2, 3]
where we are using the Scribe module for formatted output printing.
Similarly, we have mapping and transformation functions like:
local a = Array{1,0,2,0,0,3}
local b = a:map(function(x) return 2*x end)
("%s doubles to %s", a, b) putln
This outputs [1, 0, 2, 0, 0, 3] doubles to [2, 0, 4, 0, 0, 6]
.
As shown, these methods are happy to be passed complete Lua function definitions, which allow you to do arbitrarily complex things. However, the arguments to higher-order functions like these are often quite simple, so we also support the idea of functions as string operators and string lambdas—anonymous functions, if you will.
The first example above can be written in a more straightforward and more readily understood form:
local a = Array{1,0,2,0,0,3}
local non_zero = a:take_if(">", 0)
("%s has non-zero elements %s", a, non_zero) putln
The second becomes:
local a = Array{1,0,2,0,0,3}
local b = a:map("|x| 2*x")
("%s doubles to %s", a, b) putln
Or even more, simply using anonymous arguments (i.e. using _
in place of the named argument x
)
local a = Array{1,0,2,0,0,3}
local b = a:map("2 * _")
("%s doubles to %s", a, b) putln
Module Methods and Data
If you have imported the module as
local callable = require 'lulu.callable'
You have access to the following methods.
callable.from(fun, fatal_on_error)
This is the master method that tries to return a Lua function from the fun
argument, which can be:
- Al Lua function.
- A callable object like a
table
with a__call(...)
metamethod. - A string that we can interpret as a lambda function.
- A string that we can interpret as a standard Lua operator.
If the optional boolean fatal_on_error
argument is true
(which it is by default), then we raise a fatal error if fun
cannot be interpreted as a function.
The simple function call callable(fun) is identical to callable.from(fun) .
|
callable.is_callable(fun)
Returns true
if the fun
argument will return a Lua function when passed to callable.from(fun)
. callable[op]
The callable
module also maps standard operator “names” to their definition in the form of a Lua function.
Key | Corresponding Lua Function |
---|---|
callable['+'] |
function(a,b) return a + b end, |
callable['-'] |
function(a,b) return a - b end, |
callable['*'] |
function(a,b) return a * b end, |
callable['/'] |
function(a,b) return a / b end, |
callable['%'] |
function(a,b) return a % b end, |
callable['^'] |
function(a,b) return a ^ b end, |
callable['=='] |
function(a,b) return a == b end, |
callable['~='] |
function(a,b) return a ~= b end, |
callable['<'] |
function(a,b) return a < b end, |
callable['<='] |
function(a,b) return a <= b end, |
callable['>'] |
function(a,b) return a > b end, |
callable['>='] |
function(a,b) return a >= b end, |
callable['and'] |
function(a,b) return a and b end, |
callable['or'] |
function(a,b) return a or b end, |
callable['()'] |
function(fn, ...) return fn(...) end, |
callable['{}'] |
function(...) return {...} end, |
callable['[]'] |
function(t, k) return t[k] end, |
callable['#'] |
function(a) return #a end, |
callable['..'] |
function(a,b) return a .. b end, |
callable['~'] |
function(a,b) return string.find(a, b) ~= nil end, |
callable[''] |
function(...) return ... end |
We had the example above a:take_if(">", 0)
. The take_if
signature is take_if(fun, ...)
and that call works as follows:
- The
callable
module looks up that first string argument “>” in its table and finds a corresponding valuefunction(x,y) return x > y end
, a function of two argumentsx
andy
. - Then
take_if
calls that function asfun(a[i], ...)
iterating through the elements ofa
for the first argument and passing any trailing arguments it received after that. There was just one of those — the number0
.
You can call Lua functions with many more arguments than they expect, and those are just ignored. |
callable.lambda(str, issue_warnings)
This tries to create a Lua function using either named or anonymous arguments from a lambda string.
If the optional boolean issue_warnings
argument is true
(which it is by default), then we issue a warning if fun
cannot be interpreted as a function and then return nil
.
The syntax used is similar to that from other languages like Rust: |args| body
or body
with _
marking a single argument.
Examples
local f = callable.lambda("|a| a + 1")
local g = callable.lambda("|a,b| a * b")
local h = callable.lambda("_ + 1")
print(f(10), g(10,11), h(10))
Outputs 11 110 11
as expected.
The shorthand callable("|a| a + 1") will return the same function as callable.lambda("|a| a + 1")
|
String | Lua Function |
---|---|
|a| a + 1 |
function(a) return a + 1 end |
|a, b| a * b |
function(a, b) return a * b end |
_ + 1 |
function(_) return _ + 1 end |
Acknowledgement
Like many items in the lulu
library, this was inspired by Penlight.