Created
February 17, 2021 03:35
-
-
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?
This file contains 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
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