maiX2
maiX2

Reputation: 13

Function argument as another function's name?

I'm trying to tidy up some code a bit and to create a function generator. So I'm passing a bunch of arguments to a function creating a nested function; first of them is the desired name of the nested function.

function gen (name)
return
 {
 function name ()
 return
  {
  ...
  }
 end
 }
end

Then I call gen function a couple times with different names to get multiple name functions with different values inside. And obviously it doesn't work as I want it to. The exact error I'm getting is

'(' expected near 'name'

Surprisingly when I use a name = function () notation I get some other error far downstream. I've played with the code a little, but my knowledge is not enough to use any construct but the initial extremely bulky version.

Update #1 This

function gen (self, name, arg2)
return
{
 [name] = function (arg3)
 return
 {
   data = arg3
 }
 end
}
end
for k,v in pairs (gen (name, "xarg")) do print(k,v) end
print (xarg)

returns

xarg    function: 0x1365390
nil

And with _G[name] I have no luck at all.

Update #2

So I haven't found a solution and switched to use of t[name] at the cost of discarding the (arg3).

Upvotes: 1

Views: 1255

Answers (2)

tehtmi
tehtmi

Reputation: 741

There are some basic misunderstandings here, so I'll try to break it down step by step. Consider the first part of the initial given code.

function gen (name)  -- define a function named "gen"
return               -- return
 {                   --        a table
 function name ()    --                containing a function...
-- et cetra ...

When you write a pair of matched curly braces {...} in Lua you are defining a table. Things you write inside the braces become the contents of the table. This is called a table constructor.

The error you are getting in this code is because you have tried to write a statement where Lua expects an expression. The difference between these things is somewhat technical, but basically, expressions represent a value and may contain things like numbers, strings, operators, table constructors and function calls. A statement is a piece of code that represents an action like assigning an expression to a variable, a function call (which can be an expression or a statement), or a complex structure like an if or while block (which probably has other statements inside).

function name() ... end is a statement in Lua because it assigns the function to the global variable name. It is also possible to have an function as an expression, but then you can't give it a name (in the same way). This looks like function() ... end. That's why Lua said it was expecting ( after function. Giving an expression a name in Lua in the same thing as an assignment, so it is a statement.

This may be a little bit confusing in the case of a table constructor, as you can put something that looks a lot like an assignment statement inside of a table constructor. For example {x = 3} or {name = function() ... end} are perfectly fine expressions, but here the x = or name = means something different than it does as a statement outside of a table constructor. It doesn't mean to assign to a variable called x or name, but instead to add a field to the table with that name.

For example:

table1 = {x = 3}
print(x) -- nil
print(table1.x) -- 3

table2 = {name = function() print("hello!") end}
name() -- error!
table2.name() -- hello!

Now, consider

function gen(name)
    function name()
       print("hello!")
    end
end

When you put an identifier between function and (), Lua uses literally what you wrote as the name of the function. So:

gen("f")
f()    -- error
name() -- hello!

You need to do something special to assign a value to a variable that is determined at run-time. For local variables, this is possible only with the debug API (and only then for variable names that already exist otherwise). For global variables, you can use the global table.

When you assign a value to a global variable, Lua internally has to keep track of it somehow. It does this using a table, just the same as any other table. By default, the variable _G contains a reference to that table.

print(x)           -- nil
print(_G.x)        -- nil
x = 3
print(x)           -- 3
print(_G.x)        -- 3
_G.x = 4
print(x)           -- 4
print(_G.x)        -- 4
print(_G == _G._G) -- true

If used as I did above, this isn't very useful. However, remember that in Lua, table.x is actually shorthand for table["x"]. By using the [] syntax instead of the . syntax, we gain the flexibility to do define a global variable dynamically.

_G["y"] = 5
print(y)          -- 5
print(_G.y)       -- 5
print(_G["y"])    -- 5
print(z)          -- nil
name = "z"
_G[name] = 6
print(z)          -- 6

This is essentially what lhf's answer does. It defines assigns a function to a global variable with a name determined by the argument of the surrounding function. It should work perfectly if you want to define a global function.

Now consider your second answer block of code:

function gen (self, name, arg2)
return
{
 [name] = function (arg3)
-- et cetra ...

Here again, we are in a table constructor, so we are not defining a global variable, we are creating a table field whose value is a function. As before, using the [] syntax allows the field's name to be determined by the name variable. Consider:

table = gen(nil, "xarg")
-- table = {xarg = function(arg3) return {data = arg3} end}
print(table.xarg)                  -- function ...
table2 = table.xarg("mydata")
print(table2.data)                 -- mydata
table3 = table.xarg("myotherdata")
print(table3.data)                 -- myotherdata
print(table2 == table3)            -- false

-- you can assign this to a global variable if you want later
-- (but then why put it in a table to begin with?)
xarg = table.xarg
print(xarg("mythirddata").data)    -- mythirddata

name2 = "xarg2"
table_xarg2 = gen(nil, name2)
_G[name2] = table_xarg2[name2]
print(xarg2("mylastdata").data)    -- mylastdata

This also works fine if your intent is to for the function to be stored in the table.

You have to decide where you want your function to be stored, and put it there. In either the global environment or as a field in a table, you can choose the name based on the argument of a function.

Note that the function itself doesn't have a name. Lua functions don't inherently have names. They are just (usually) stored in variables or fields, and the name of the variable or field is thought of as the name of the function.

Another possibility, then, is to not give a name inside the generator at all. For example:

function gen(arg1)
  return function(arg2)
    return {data1 = arg1, data2 = arg2}
  end
end
xarg1 = gen("a1")
xarg2 = gen("a2")
record1 = xarg1("b1")
record2 = xarg2("b2")
print(record1.data1)      -- a1
print(record1.data2)      -- b1
print(record2.data1)      -- a2
print(record2.data2)      -- b2

Upvotes: 2

lhf
lhf

Reputation: 72312

If you want to create a global function named name, then use

function gen (name)
  _G[name] = function ()
               -- body here
             end
end

Upvotes: 1

Related Questions