lua-users home
lua-l archive

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


> I doubt the latter is identical to the proposed
> "foreach" function because of scope, although I could be wrong.

  The reason why Lua 3.0 doesn't have nested functions is scope. At one
hand, it is almost useless to be able to write a nested function without
access to local variables of the enclosing environment. On the  other hand,
it is well known the problem of "dangling references" to these variables,
like below:

    function f(x) return function (y) return x+y end end

    a = f(4)
    print(a(5))   -- where/what is "x"??


  In Lua 3.1, we are proposing a solution to this problem which we have
called "upvalues" (don't pay much attention to the name...). An upvalue
is an expression of the form "%name", which is evaluated when a function
is *created*, and not when it is called. "name" can be any variable
visible at the point where the function is created, that is, a local
variable of the enclosing environment or a global variable. With this,
the above example could be written as:

    function f(x) return function (y) return %x+y end end

Now, when "f(4)" is executed, the inner function is created and the "%x" is
evaluated to the constant "4"; so "a" will get a function like
"function (y) return 4+y end", and "a(5)" will give the correct result.
(Notice that if you write "x+y" in the function body you get a
compiler error, since 'x' is out of scope; therefore it is not that
difficult to remember to use the '%'. On the other hand, the '%' reminds
that this is not an usual variable access.)

  Function "foreach" will be pre-defined, but it could be defined in Lua
as well (the pre-defined version is much more efficient):

    function foreach (t, f)
      local i, v = next(t, nil)
      while i do
        local r = f(i, v)
        if r then return r end
        i, v = next(t, i)
      end
      -- return nil
    end

(a similar "foreachvar" will traverse global variables, with "nextvar").

  With those facilities, Lua 3.1 will support code like:

    -- cloning a table 't':
    local new = {}; foreach(t, function (i, v) %new[i] = v end)

    -- searching an index with a given value 'value':
    i = foreach(t, function (i, v) if v == %value then return i end end)
    -- if i==nil then value not found

    -- counting the number of elements in a table:
    count = {n=0}; foreach(t, function() %count.n = %count.n + 1 end)
    -- (notice that upvalues cannot be assigned, so the use of a table
    -- field 'n')

    -- "quick and dirt" print of a table contents:
    foreach(t, print)

  Therefore, we hope that "foreach" will be able to replace most current loops
that use "next". (Nevertheless, we think that *if* Lua gets a "for"
constructor, this constructor should allow the traversal of tables...)


  Obviously, nested functions + upvalues are very useful in many other
uses not related to traversing tables. For instance:

    -- collecting all words in a string:
    local words = {n=0}
    gsub(s, "(%w%w*)", function (w)
                         %words.n=%words.n+1; %words[%words.n] = w;
                       end)

    -- avoiding (accidental) redefinition of pre-defined functions:
    settagmethod(tag(print), "setglobal",
                 function(n) error("cannot redefine function "..n) end)


  Finally, upvalues have other uses independently of nested functions.
For instance, one can redefine a standard function:

print = function (s)
          if strlen(s) > 1000 then error("string too long to print") end
          %print(s)   -- upvalue!
        end

  The "%print" is evaluated when the function is declared, and its value
is then fixed to the old print function. This old function, however, is
not accessible from any other part of the program, since the global name
"print" has been redefined.


-- Roberto

PS: with nested functions, one could "define" for constructors, with
endless variations, like:

   function for (i, e, f)
     while i<e do f(i); i=i+1 end
   end

PS2: we must keep in mind that a "for" construct creates a small
compatibility problem, since "for" will be a reserved word (that's why
we `reuse' the keyword "then" in our proposal...).