4

I have been playing with Lua for the past week and I ended up writing this peace of code. I find it really useful that you can dynamically create new functions that "inherit" other functions, so programmers must have a name for it. My questions are:

What is this called?

Is there a better way to do this? (cycle over a data structure and create add-on functions that "improve" existing functions)

D = {
  name = {
    value = nil,
    offset = 0,
    update = function (self)
      self.value = "Berlin"
    end,
  },
}

--Dynamic function definition
for i in pairs(D) do
  D[i].upAndWrite = function(self)
    self:update()
    print("upAndWrite was here")
  end
end

print(D.name.value)
D.name:upAndWrite()
print(D.name.value)

Result:

nil
upAndWrite was here
Berlin

2 Answers 2

8

I don't think that what you're doing have special name for it, it's just on-the-fly function creation.

There are few notes regarding your code:

Proper for loop

for i in pairs(D) do
…
end

In programming, variable i is generally used for counter loops, like in

for i=1,100 do
…
end

Here, pairs returns an iterator function and the idiomatic way to use it is

for k,v in pairs(D) do
…
end

Here k is a key (like i in your code) and v is a value (use it instead of indexing table like D[k] or D[i] in your code when you need to access the corresponding value).

There's no need to create functions on the fly!

Another important thing is that you create new function on each loop iteration. While this feature is very powerful, you're not using it at all as you don't store anything using upvalues and only access data through arguments.

A better way to do it would be creating function once and assigning it to every field:

-- Define D here

do
    local function upAndWrite(self)
        self:update()
        print("upAndWrite was here")
    end

    for k,v in pairs(D) do
        v.upAndWrite = upAndWrite -- Use our function
    end
end

-- Perform tests here

What does on-the-fly function creation allow?

As mentioned above, you can utilize this very powerful mechanism of closures in certain situations. Here's a simple example:

local t = {}

for i = 1, 100 do
    -- Create function to print our value
    t[i] = function() print(i) end

    -- Create function to increment value by one
    t[-i] = function() i=i+1 end
end

t[1]()   -- Prints 1
t[20]()  -- Prints 20
t[-20]() -- Increment upvalue by one
t[20]()  -- Now it is 21!

This example demonstrates one possible usage of upvalues and the fact that many functions can share them. This can be useful in a variety of situations together with the fact that upvalues can't be changed by side code (without use of debug library) and can be trusted in general.

I hope my answer covers what you wanted to know.

Note: Also, this language is called Lua, not LUA.

Sign up to request clarification or add additional context in comments.

1 Comment

You give a good example of evaluating a function definition in a loop to yield distinct function values. But, it could be obscuring the fact that no function value is created from any function definition except when the execution flow path evaluates the function definition. In other words, all function values are created "on-the-fly."
4

As a whole it doesn't have a name, no. There's lots of concepts that play into this:

  • First Class Functions aka. functions that can be assigned to variables and passed around just like numbers or strings.
  • Anonymous Functions aka. functions that are created without giving it a name explicitly. All functions in Lua are technically anonymous, but often they are assigned into a variable right after creation.
  • Metaprogramming aka. writing programs that write programs. A loop that creates functions (or methods) on an arbitrary number of objects is very simple, but I'd count it as metaprogramming.
  • Lua Tables; this may seem obvious, but consider that not all languages have a feature like this. Javascript has objects which are similar, but Ruby for example has no comparable feature.

If you're gonna use pairs, you might as well make use of both variables.

for key, object in pairs(D) do
  function object:upAndWrite(self)
    self:update()
    print("upAndWrite was here")
  end
end

Though that would create many closures, which means more work for the garbage collector, more memory usage and slower execution speed.

for key, object in pairs(D) do
  print(object.upAndWrite) -- All the functions are different
end

It's a good first stage, but after refactoring it a bit you could get this:

do
   local method = function(self) -- Create only one closure
      self:update()
      print("upAndWrite was here")
   end

   for key, object in pairs(D) do
      object.upAndWrite = method -- Use single closure many times
   end
end

Now there's only one closure that's shared among all the tables.

for key, object in pairs(D) do
  print(object.upAndWrite) -- All the functions are the same
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.