abortive experiment: keep definitions independent
[?]
Apr 16, 2023, 12:22 AM
H3RX6UWRIBSSGIKCHCYBW2UMW63HLV77BULUXS3BIBKKBEOJ64EQCDependencies
- [2]
FFFJ54GJrestore globals after running tests - [3]
UY647VAQrename - [4]
NVEA3SMUreorg - [*]
LRDM35CEapp running again - [*]
WYKKFV2Gprevent overriding foundational definitions - [*]
3BRGOF7Nreturn failing tests with every code change
Change contents
- edit in live.lua at line 39
Live.initial_env = table.copy(_G) - edit in live.lua at line 261
-- pre-check - edit in live.lua at line 266
-- perform the real evaluation and send back any real errors - edit in live.lua at line 279[8.30][2.9]
-- Try running in bare environment to catch any order-dependence in-- definitions. The basic intent is to avoid this situation:-- define X = 3-- define Y = X+1-- edit X = 4-- Now the definition of Y runs before X (because Freewheeling apps load-- top-level definitions in version order) when X is nil, and Y starts-- raising a confusing error. And all because we modified X. Action at a-- distance.---- To avoid this we make sure each definition is always self-contained.---- This feels highly experimental. Concerns:---- 1. Any side-effects in top-level definitions will run twice. Ugh.-- But then, they will also run any time you restart the app.-- Putting side-effects in the top-level seems like a more unnatural thing-- for a programmer to do than defining one global in terms of another.---- 2. We still won't catch situations where we depended on a global-- variable and it used to be non-nil but now it's nil:-- define X = 3-- define Y = X-- edit X = 4-- Now Y is nil when it didn't used to be. Which can be confusing. And it-- won't even raise any errors.---- 3. I'm going to create only a shallow copy of globals. It's hard to-- think about what real, really humongous global variables we might end-- up accidentally copying and turning everything sluggish. We might still-- miss some situation like:-- define io.bar-- define Y = io.bar -- allowed because io.bar has leaked in to the bare environment-- So this isn't perfect.---- 4. Lua's setfenv is confusing to me. I wonder if there's some situation-- like:-- X = foo()-- that might cause trouble because foo() will continue to use the-- non-initial environment.---- So far it seems fine. My intent is only to catch order-dependence in-- top-level definitions. The initial environment will have a subset of-- definitions; we prevent the live app from mutating any of them. It seems-- reasonable to assume that any pre-existing functions will not rely on any-- globals some random app is going to introduce.---- 5. I could probably move the setfenv into eval_in_initial_env, but that-- feels confusing to think about given the table of bar envs is itself-- stored in a global variable.---- 6. I could probably avoid duplicating eval as eval_in_initial_env, but-- it feels less error-prone to keep the two isolated. We never mess with-- the env of the real eval.setfenv(live.eval_in_initial_env, table.copy(Live.initial_env))local status, err = live.eval_in_initial_env(buf)if not status thenlive.roll_back()live.send_to_driver('ERROR this definition depends on other globals which can lead to hard-to-debug errors. Please keep top-level definitions order-independent. Define functions for more complex initialization.')returnend - edit in live.lua at line 428
end-- exact copy of live.eval that has had setfenv applied to itfunction live.eval_in_initial_env(buf)local f = load('return '..buf, 'REPL')if f thenreturn pcall(f)endlocal f, err = load(buf, 'REPL')if f thenreturn pcall(f)elsereturn nil, errend - edit in live.lua at line 444
function table.copy(h)local result = {}for k,v in pairs(h) doresult[k] = vendreturn resultend - edit in live.lua at line 452
function table.length(h)local result = 0for _ in pairs(h) doresult = result+1endreturn resultend