Functions can refer to each other, but global variable initializers shouldn't.
But this doesn't work. That comment keeps growing to capture more corner cases.
Step back. What am I trying to achieve?
I'm not trying to create a better abstraction for programming with. I'm trying to use an existing abstraction (LÖVE) without needing additional tools.
I'm not supporting end-user programming, only end-programmer programming. What happens in a regular LÖVE program if you use a global before it's defined? You get an error, and you're on the hook to fix it. But it's obvious what's going on because a file has an obvious sequence of definitions. But what if you have multiple files? It's easy to lose track of order and we mostly don't care.
The important property existing dev environments care about: merely editing a definition doesn't change the order of top-level definitions. Let's just provide this guarantee.
We'll no longer load definitions in order of their version. Just load definitions in the order they were created. Editing a definition doesn't change this order. Deleting and recreating a definition puts it at the end.
H3RX6UWRIBSSGIKCHCYBW2UMW63HLV77BULUXS3BIBKKBEOJ64EQC
FFFJ54GJ3A2HNKZEHO7RHUZ2YWT67EK4B3Y2YJVYCEUANGY6BQQAC
UY647VAQW72BNAUPRRREATG54F44WAXAY3SXZVWZSDVHFU4OZJOAC
NVEA3SMUPTZBTM76PANXZON47KVZ4KVIDOZCP5NQQKJ2OI22JX2AC
WYKKFV2GP7JRPN4SCWTHECFCQCHCIOMUP2TNNX5YACQAEKJ5QP5QC
LRDM35CEK3OHXOTB7TEFJRL7P6PQWO5ZG3F2BVA7DIDFHBPJQ7KAC
3BRGOF7NV52C3CY6HLGH53TDW2OHRQYQHWBEA3P6BKCUTN5DVHQQC
-- 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 then
live.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.')
return
end