lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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