[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Fun with function wrapping
- From: Bret Victor <bret@...>
- Date: Thu, 8 Feb 2007 08:06:09 +0000 (UTC)
So, that post about redefining "require" got me thinking about
function wrapping. It would be neat to have a function "wrap"
such that you could write the "require" redefinition as:
require = wrap(require, function (f)
local wasInsideRequire = isInsideRequire
isInsideRequire = true
f()
isInsideRequire = wasInsideRequire
end)
In general, you pass "wrap" a "wrappee" and a "wrapper":
wrappedFoo = wrap(foo, function (f)
stuffToDoBeforeFoo()
f() -- foo gets called here, somehow
stuffToDoAfterFoo()
end)
The tricky part is that all of the arguments passed to "wrappedFoo"
have to be shuttled into "foo", and all the results of "foo" have
to be shuttled out of "wrappedFoo"!
Here's the straightforward implementation:
function wrap (wrappee,wrapper)
return function (...)
local args = { ... }
local results
wrapper( function ()
results = { wrappee(unpack(args)) }
end)
return unpack(results)
end
end
That works, but there's sure a lot of unpacking going on. I wondered
if it could be implemented without tables, keeping everything on the
stack. After some thought, I came up with this crazy thing:
function wrap (wrappee,wrapper)
return function (...)
local cowrapper = coroutine.wrap(wrapper)
local thruCo = function (...)
cowrapper(coroutine.yield)
return ...
end
return thruCo( wrappee ( thruCo(...) ) )
end
end
That keeps everything on "the stack". It cheats by creating a new stack!
But hey, it's late, and that's close enough for me.
Anyone else have an interesting take on this problem?
-Bret