Skip to content

Instantly share code, notes, and snippets.

@trentgill
Last active August 21, 2025 23:01
Show Gist options
  • Save trentgill/9e84f777b0ba976938972dda91d47024 to your computer and use it in GitHub Desktop.
Save trentgill/9e84f777b0ba976938972dda91d47024 to your computer and use it in GitHub Desktop.
write quick anonymous functions in lua
-- lua funtimes!
-- tiny functions that make writing anonymous functions way faster
-- no runtime overhead vs writing them yourself
-- inspired by fennel's hashfn syntax
-- useful for functional programming where you need to create small transformer functions
-- or more generally for partial application of arguments
-- eg: function makenote(pitch, velocity)
-- makepitch = fn'makenote($, 1)
-- makepitch(1.3) --> calls makenote(1.3, 1)
-- but note the above is just to explain. you generally don't want to assign these to variables
-- instead, just plug them into other functions expecting function arguments
-- here's a demo using it with table.sort to sort a table alphabetically
-- table.sort(a, fn'string.lower($1) < string.lower($2)')
-- there are (currently) 3 functions:
-- fn(str) converts the string to a function returning the (last) value created
-- fnB(str) converts the string to a function that doesn't return (void fn)
-- op(str) returns a function that performs the provided operator's function
--- limitations
-- currently only supports 3 arguments. trivial to add more, but not sure if it adds runtime overhead
-- if you need more than 3 args, you should probably write a real function rather than modify this lib
-- error handling is poor. if you don't *know* it's correct, then write a real func
--- usage
-- $: the first argument
-- $n: the nth argument ($ is alias for $1)
local function examples()
addOne = fn'$ + 1'
squared = fn'$ * $'
mac = fn'$1 * $2 + $3'
lerp = fn'$1 + $3 * ($2 - $1)'
chain = fn'print "the answer"; 42'
-- results
addOne(3) --> 4
squared(2) --> 4
mac(2,3,4) --> 10
lerp(1,2,0.5) --> 1.5
chain() --> [prints: the answer] 42
do_cmd = fnB'print "hi"'
do_2cmds = fnB'print "hello"; print "world"'
-- results
do_cmd() -- prints: hi
do_2cmds() -- prints: hello\nworld
add = op'+'
lshift = op'<<'
-- results
add(3,4) --> 7
lshift(4,2) --> 16
end
local ft = {}
function ft.fn(str)
-- NOTE: we replace numbered elems first
-- then do bare $, as it would capture the former
str = str:gsub('%$(%d)', '_Z%1') -- replace $n with _Zn
str = str:gsub('%$', '_Z1') -- replace $ with _Z1
local last_statement = str:match('.*();')
if last_statement then
str1 = str:sub(1, last_statement)
str2 = str:sub(last_statement+1)
str = string.format('return function(_Z1,_Z2,_Z3) %s return %s end', str1, str2)
else
str = string.format('return function(_Z1,_Z2,_Z3) return %s end', str)
end
-- note we have to wrap our anon fn in a block, then call it to get the fn
return load(str)()
end
-- no return
function ft.fnB(str)
str = str:gsub('%$(%d)', '_Z%1') -- replace $n with _Zn
str = str:gsub('%$', '_Z1') -- replace $ with _Z1
print(string.format('return function(_Z1,_Z2,_Z3) %s end', str))
return load(string.format('return function(_Z1,_Z2,_Z3) %s end', str))()
end
ft._ops = {
['+'] = function(a,b) return a+b end,
['-'] = function(a,b) return a-b end,
['*'] = function(a,b) return a*b end,
['/'] = function(a,b) return a/b end,
['//'] = function(a,b) return a//b end,
['%'] = function(a,b) return a%b end,
['^'] = function(a,b) return a^b end,
['-'] = function(a) return -a end,
['&'] = function(a,b) return a&b end,
['|'] = function(a,b) return a|b end,
['~'] = function(a,b) return a~b end,
['>>'] = function(a,b) return a>>b end,
['<<'] = function(a,b) return a<<b end,
['=='] = function(a,b) return a==b end,
['~='] = function(a,b) return a~=b end,
['<'] = function(a,b) return a<b end,
['>'] = function(a,b) return a>b end,
['<='] = function(a,b) return a<=b end,
['>='] = function(a,b) return a>=b end,
}
function ft.op(str)
return ft._ops[str]
end
-- a hack to pull them into global namespace
-- for k,v in pairs(ft) do
-- if type(v) == 'function' then
-- _G[k] = v
-- end
-- end
-- normal return style for require
return ft
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment