need to use regular functions for `rec {}` getters but arrow functions for `{}` getters, to ensure that using `this` for property access works correctly
ADD TO TESTS:
let a = 0; in rec { a = 1; foo = bar: a * 2 }
# 2
let a = 0; in rec { a = 1; foo = let func = bar: a * 2; in rec { a = 3; baz = func; }; }
lazy-wrapping expressions which are object/attrset properties, array/list elements, function args, bindings(!), and so on (and unpacking them on the corresponding accesses) but otherwise just directly translating to JS
Note: equality comparison for lists and attrsets is a *deep* comparison by default! Need an equality operator wrapper to handle this. For derivations, equality is determined by the outPath. Functions are never equal, and the only type compatibility is between integers and floats.
----
General wrapping concept: expressions are wrapped in a lazy eval wrapper when they are either bindings or members of a complex data structure, where evaluating the data structure itself *does not* imply evaluating its members. The "set of function args in a function call" counts as a complex data structure.
Binding access can be blindly unwrapped because regardless of whether it's a local binding or eg. originates from a `with` statement (that takes an object), the binding itself will always contain a wrapped expression.
Passing around wrapped unevaluated expressions (eg. assigning an object property to *another* object property, or passing an object property into a function call) is made possible in a somewhat hacky way; instead of first unwrapping (evaluating) the source expression and then passing the result somewhere, the evaluation *itself* will (according to the above principles) be wrapped in a lazy wrapper, meaning that the evaluation of the source expression will only take place if the *new* wrapper is evaluated somewhere.
This means that you end up with something along the lines of `someFunc(() => otherWrapper())` - the immediate call from within a wrapper might *seem* redundant, but therefore isn't. It may be possible to improve on this in the future, but for now this significantly reduces compiler complexity, because the compiler does not need to track provenance of the source expression to determine when it can be passed in directly as-is, and does not need to do code analysis to determine whether it is safe to do so in all cases. Let's just let V8 worry about optimizing this for now.