[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: '_ENV.foo' mandatory for non-local assignment.
- From: Henk Boom <henk@...>
- Date: Sun, 23 Jan 2011 13:13:36 -0500
2011/1/22 Pierre-Yves Gérardy <pygy79@gmail.com>:
> There has already been a lot of discussion regarding the default variable
> scope, but I don't think that this idea has been proposed.
> The new _ENV system enables a way to consistently define non-locals
> (hereafter globals) without introducing a 'global' keyword.
> It is a bit clunky, but since using globals is most of the time a bad
> practice, making them stand out isn't a problem IMO.
> Reading globals would remain the same.
> This would allow to remove the 'local' keyword (I know, it's controversial,
> I've read the wiki page [1]. I will address the technical aspects discussed
> there).
> Introducing explicit upvalues would allow the same scoping flexibilty as the
> current system, with added clarty.
> This should speed up the compiler a bit, since it wouln't have to check
> whether a variable not declared in a given scope is a global or an upvalue.
> It would also allow to throw a compile time error if an upvalue isn't
> found.
> You could still declare locals when needed by assigning 'nil' to them.
> Here are some suggestions for the syntax.
> a,b = nil -- declare the locals at the top of the main block. Only one
> nil is needed for the list.
> function foo ()
> upval a, b = 5, 6 -- upval as keyword. Compile time error if they
> are not found.
> ...
> end
> function bar ()
> return upval.b -- shortcut: upval as a pseudotable
> end
>
> I see on drawback to requiring _ENV.foo for assignement : modules. A naming
> convention could be recommended: '__' as the module table, for example.
> Kind regards,
I do think that with the new _ENV semantics there's more flexibility
to protect against the accidental use of globals, and that this is
worth exploring.
However, I don't feel that declaring variables on use instead of on
creation is win. It ubfuscates scoping since it's harder to see
declarations. It also doesn't really make much sense for lua, since
upvalues are only introduced at function scopes while locals exist in
much more finely grained scopes. That is, upvalues and scope are
entirely different issues.
function f()
local x = 0
do
-- this is a new scope, but technically x is not an upvalue
x = 1 -- so is it local? do I need to declare it with upval anyways?
end
assert(x == 1)
end
As an experiment, I took the update() function from some code that I had here:
https://github.com/henkboom/pax-britannica/blob/master/scripts/fighter_ai.lua
I manually declared all of the variables from higher scopes as
upvalues, then removed all of the local declarations. The result is a
bit more verbose, and more importantly I have a lot of trouble
figuring out what the scopes of variables are (search for 'upval' to
see how many are needed):
function update()
upval target
-- if we go from on to off screen, retarget
upval on_screen
new_on_screen = game.targeting.on_screen(self.transform.pos)
if (on_screen and not new_on_screen)
or not target
or target.dead
or math.random() < 0.005 then
retarget()
end
on_screen = new_on_screen
if target then
upval target
game.tracing.trace_line(self.transform.pos, target.transform.pos)
end
if target then
upval target
to_target = target.transform.pos - self.transform.pos
dist_squared = v2.sqrmag(to_target)
if running then
upval run_distance
upval dist_squared
-- run away until you have full ammo and are far enough away
too_close = dist_squared < run_distance^2
-- if you're too close to the target then turn away
if too_close then
upval target
self.ship.go_away(target.transform.pos, true)
else
self.ship.thrust()
end
if not self.fighter_shooting.is_empty() and not too_close then
upval running
running = false
end
else
-- go towards the target and attack!
upval target
self.ship.go_towards(target.transform.pos, true)
-- maybe shoot
if self.fighter_shooting.is_ready_to_shoot() then
upval shot_range
upval to_target
upval dist_squared
facing = self.transform.facing
if dist_squared <= shot_range * shot_range
and v2.dot(to_target, facing) > 0
and v2.dot(to_target, facing)^2 > 0.97 * dist_squared then
self.fighter_shooting.shoot()
end
end
-- if out of shots then run away
if self.fighter_shooting.is_empty() then
running = true
end
end
end
end
henk