lua-users home
lua-l archive

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

On Apr 11, 2000 Edgar Toernig wrote:

> in a previous mail I talked about allowing assignments to upvalues.
> Attached is a patch that does this.  It goes even further:
> the '%' character to introduce upvalues is removed as is the limit-
> ation to only access locals of the first upper level.

Edgar's patch addressed two limitations of upvalues:

    - they may not be modified
    - only the next enclosing scope may be accessed

Before reading his post, I had not realized that upvalues had these
limitations.  At the time of the post my knowledge of Lua was mainly
from the documentation and I hadn't yet used the language much.  I
think it would be useful to clarify these limitations in the
documentation, maybe as follows (from section 4.7 in the Lua 3.2
manual, modifications and additions marked with []):

  An upvalue is somewhat similar to a variable expression, but whose
  value is frozen when the function wherein it appears is
  instantiated. The name used in an upvalue may be the name of any
  variable [sharing the scope of the function definition.  An upvalue
  may not be modified.]

  Here are some examples:

    a,b,c = 1,2,3   -- global variables
  [ local d                                                          ]
    function f (x)
      local b       -- x and b are local to f
      local g = function (a)
        local y     -- a and y are local to g
        p = a       -- OK, access local 'a'
        p = c       -- OK, access global 'c'
        p = b       -- ERROR: cannot access a variable in outer scope
        p = %b      -- OK, access frozen value of 'b' (local to 'f')
        p = %c      -- OK, access frozen value of global 'c'
        p = %y      -- ERROR: 'y' is not visible where 'g' is defined
  [     p = %d      -- ERROR: 'd' is not in same scope as 'g'        ]
  [     %b = y      -- ERROR: cannot modify an upvalue               ]
      end           -- g
    end             -- f

Also I want to take a stab at a "simple" explanation of why the %
prefix is necessary, because this point seems to confuse people:

  Some languages, such as Scheme, are statically scoped.  In effect
  this means that when a function references a variable outside its
  scope, a private copy of the variable is made at the time the
  function is defined.

  In contrast, languages such as Python are dynamically scoped.  In
  this case when a function references a variable outside its scope,
  no copy is made when the function is defined.  Instead that
  reference is "re-bound" every time the function is called.  In
  Python this is quite inefficient because the scopes surrounding the
  function must be searched for the variable name one by one.

  Lua however, supports both scoping methods.  The default is dynamic
  scoping.  However unlike Python only global variables may be
  referenced in this manner, which avoids the performance issue with
  searching nested scopes.  Static scoping is supported with the %

Finally, I have a few opinions on upvalues.  The "anti-global"
programmers out there may not like having to prefix all references to
the outer scope with % since that will be their most common usage, but
in practice I haven't found this to be much of a bother.  Given Lua's
original purpose as a simple configuration language it's easy to
understand why dynamic scoping is the default.

As for the limitation about only being able to access the next
enclosing scope, while it makes me a little uncomfortable it seems to
at least cover the most common usage.

However, not being able to modify upvalues is quite painful in terms
of the effect on my code.  It is especially frustrating since
functional programming is encouraged by the existence of foreach(),
etc.  For example take this segment from the function overloading
program I posted yesterday:

  function get_typestring( list )
    local closure = { typestring = "" }
    local f = function( index, value )
      local typename = type(value)
      if (typename == "nil") then typename = "0" end
      %closure.typestring = %closure.typestring..strsub(typename,1,1)
    foreachi( list, f )
    return closure.typestring

If upvalues could be modified it would become:

  function get_typestring( list )
    local typestring = ""
    local f = function( index, value )
      local typename = type(value)
      if (typename == "nil") then typename = "0" end
      %typestring = %typestring..strsub(typename,1,1)
    foreachi( list, f )
    return typestring

-John Belmonte