Skip to content

Instantly share code, notes, and snippets.

@bmwalters
Last active April 17, 2016 01:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmwalters/065c57e4f3d5b60b607479b30748debd to your computer and use it in GitHub Desktop.
Save bmwalters/065c57e4f3d5b60b607479b30748debd to your computer and use it in GitHub Desktop.
Lua SuperFuncs!
require("superfuncs")
-- example_variables
local function has_variables()
self.call = self.call + 1
print("congratulations! you are has_variables caller number", self.call)
end
has_variables = f(has_variables)
has_variables.call = 0
-- example_typecheck
local function requires_string(str)
print("we have a string!", str)
end
requires_string = f(requires_string, {"string"})
-- example_overloads
local function printit_string(str)
print("str", str)
end
local function printit_number(num)
print("num", num)
end
local function printit_anything(any)
print("any", any)
end
printit = f(printit_string, {"string"}) + f(printit_number, {"number"}) + f(printit_anything, {"any"})
has_variables() -- congratulations! you are has_variables caller number 1
has_variables() -- congratulations! you are has_variables caller number 2
has_variables.call = 999
has_variables() -- congratulations! you are has_variables caller number 1000
print(pcall(requires_string, 5)) -- false bad argument #1 to <function> (string expected, got number)
requires_string("hi") -- we have a string! hi
printit("hey") -- str hey
printit(53) -- num 53
printit({}) -- any table: xxxxxxxx
local FUNC = {}
-- calling; type checking, overloading
function FUNC:__call(...)
local args = {...}
local types = {}
if self.expected_types or (self.overloads and #self.overloads > 0) then
for i, arg in pairs(args) do
types[i] = type(arg)
end
end
-- first check all our overloads
local called = false
if self.overloads and #self.overloads > 0 then
for _, func in pairs(self.overloads) do
if not func.expected_types then error("Function overload without expected type information!") return end
if #types ~= #func.expected_types then goto continue_overloads end
for i, typ in ipairs(func.expected_types) do -- todo: prioritize funcs with less vagueness (less any)
if not (types[i] == typ or typ == "any") then goto continue_overloads end
end
func(table.unpack(args))
called = true
break -- return
::continue_overloads::
end
end
if called then return end
-- then check for our type requirements
if self.expected_types then
for i, expected_type in ipairs(self.expected_types) do
local provided_type = types[i]
if not (provided_type == expected_type or expected_type == "any") then
error("bad argument #" .. tostring(i) .. " to " .. self.fname .. " (" .. expected_type .. " expected, got " .. provided_type .. ")", 2) -- todo: expect multiple types thanks to overloads
end
end
end
-- then call ourselves
local old = _G.self
_G.self = self -- self.f
self.f(table.unpack(args))
_G.self = old
end
-- overloading
function FUNC:__add(b)
if getmetatable(b) == FUNC then
self.overloads = self.overloads or {}
self.overloads[#self.overloads + 1] = b
return self
end
error("attempt to perform arithmetic on a func value", 2)
end
-- data storage
local function_data = setmetatable({}, {__mode = "k"})
function FUNC:__newindex(k, v)
function_data[self] = function_data[self] or {}
function_data[self][k] = v
end
function FUNC:__index(k)
if FUNC[k] then return FUNC[k] end
if not function_data[self] then return nil end
return function_data[self][k]
end
-- initialization
function FUNC:init(f, types)
self.f = f
self.fname = "<function>" -- todo
self.expected_types = types
end
function f(...)
local o = setmetatable({}, FUNC)
o:init(...)
return o
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment