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.
UK4TUMBXYX5SGXO443JQ7I6LDNF62VYGBEWQF2ZFJSLS6H72EMFQC
D5KRDRYYRE577UK2HETVORW7HQQUI7ZGLDG6N56QLDDOX6GP4O5QC
LLIDXQEWTN4Y656HR54X54AFUUSEFAGJWDEMDB56ZZBKKHT5Q76QC
U5XKNGSQBZ4G5SNX5XXHZOCBVM44XBRPGURY2SDRHTA6OW5XUZ3AC
NTYQUA24YJBD45JOTFY3O4PEA4GUUV34C7MX6ORSJENBFIYACT7QC
GMDEH4RHOR6DJABQ5WVGY4GVUU3QPWFTPFUHYUOULTVJZA2FTPAQC
LDIYGOU473O3HPMOY2VDF5VTBDVUMJEB23V427KAEFGD64JGV4BAC
-- 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