Last active
August 29, 2015 14:20
-
-
Save jcmoyer/6057bccaa3c0e1315682 to your computer and use it in GitHub Desktop.
(Lua 5.3) String-lambdas that close over locals with syntax inspired by OCaml/F#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- taken from http://lua-users.org/wiki/StringTrim | |
| local function trim(s) | |
| return s:match "^%s*(.-)%s*$" | |
| end | |
| -- creates a function from a lambda string | |
| local function fun(text) | |
| -- divide the text into arg list and return body | |
| local a, r = text:match("(.-)->(.*)") | |
| a = trim(a) | |
| r = trim(r) | |
| -- we use a local, named function so that lambdas can recursively call themselves | |
| local src = string.format([[-- %s | |
| local function fun(%s) return (%s) end | |
| return fun | |
| ]], | |
| text, a:gsub('%s+', ','), r) | |
| local fe = {} | |
| -- first, inject the current _ENV | |
| for k,v in pairs(_ENV) do | |
| fe[k] = v | |
| end | |
| -- now crawl up the stack and inject any locals we find | |
| local f = 2 | |
| local i = 1 | |
| local name, val = debug.getlocal(f, i) | |
| while name do | |
| fe[name] = val | |
| i = i + 1 | |
| name, val = debug.getlocal(f, i) | |
| -- try going up one more level | |
| if not name then | |
| if debug.getinfo(f + 1) then | |
| f = f + 1 | |
| i = 1 | |
| else | |
| break | |
| end | |
| end | |
| end | |
| local chunk = assert(load(src, nil, 't', fe)) | |
| return chunk() | |
| end | |
| -- recursive factorial lambda | |
| local factorial = fun[[a s -> a > 0 and fun(a - 1, (s or 1) * a) or (s or 1)]] | |
| -- maps a function over an array table | |
| local function map(f, t) | |
| local r = {} | |
| for i = 1, #t do | |
| r[i] = f(t[i]) | |
| end | |
| return r | |
| end | |
| -- calling a local function being closed over | |
| local results = map(fun[[a -> factorial(a)]], {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) | |
| print(table.unpack(results)) | |
| -- closures really work | |
| local function f(x) | |
| local y = 2 | |
| -- although for some reason, the only way to force x and y to show up in | |
| -- debug.getlocal is to return one of them | |
| return fun[[a -> a * x + y]], x, y | |
| end | |
| -- prints 17 | |
| print(f(3)(5)) | |
| -- calling functions from a global table | |
| local cos = map(fun[[a -> math.cos(a)]], {0, math.pi / 2, math.pi, math.pi * 2}) | |
| print(table.unpack(cos)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment