Skip to content

Instantly share code, notes, and snippets.

@Corecii
Created February 17, 2021 03:35
Show Gist options
  • Save Corecii/7393f7d2000f37751d0975e372b0b580 to your computer and use it in GitHub Desktop.
Save Corecii/7393f7d2000f37751d0975e372b0b580 to your computer and use it in GitHub Desktop.
Promise- and callback-heavy code relies on passing objects around a lot, and having to enclose objects in callbacks is annoying. What if we could *almost* do `object:method` to reference a version of the method that includes the object?
local withMeta = {}
function withMeta:__index(key)
local object = self._with
return function(...)
return object[key](object, ...)
end
end
local function with(object)
return setmetatable({ _with = object }, withMeta)
end
local function makeTupleConcat(...)
local baseTupleLength = select("#", ...)
local baseTuple = {...}
return function(...)
local newTupleLength = select("#", ...)
local newTuple = {...}
local fullTupleLength = baseTupleLength + newTupleLength
local fullTuple = table.create(baseTupleLength + newTupleLength)
table.move(baseTuple, 1, baseTupleLength, 1, fullTuple)
table.move(newTuple, 1, newTupleLength, baseTupleLength + 1, fullTuple)
return unpack(fullTuple, 1, fullTupleLength)
end
end
local asIfMeta = {}
function asIfMeta:__index(key)
local object = self._with
return function(potentiallySelf, ...)
local concatArgsTuple
if potentiallySelf == self then
concatArgsTuple = makeTupleConcat(object, ...)
else
concatArgsTuple = makeTupleConcat(potentiallySelf, ...)
end
return function(...)
return object[key](concatArgsTuple(...))
end
end
end
function asIfMeta:__call(...)
local func = self._with
local concatArgsTuple = makeTupleConcat(...)
return function(...)
return func(concatArgsTuple(...))
end
end
local function asIf(object)
return setmetatable({ _with = object }, asIfMeta)
end
-- Examples:
local model = Instance.new("Model")
local destroy = with(model).Destroy
destroy()
local destroy2 = asIf(model):Destroy()
destroy2()
-- Providing the first parameters to object methods:
local someObject = {}
function someObject:log(logType, ...)
if logType == "info" then
print(...)
elseif logType == "warn" then
warn(...)
else
error(string.format("Bad logType %s", tostring(logType)))
end
end
local logInfo = asIf(someObject):log("info")
local logWarn = asIf(someObject):log("warn")
logInfo("This is an info message!")
logWarn("This is a warning", "with multiple args!")
-- Providing the first parameters to any function:
local contextualPrint = asIf(print)("[" .. script.Name .. "]")
contextualPrint("This is printing with context!")
-- Using with a promise:
Promise.delay(0.5):andThen(asIf(particleEmitter):Emit(1))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment