2

I'm trying to wrap functions with a function for protection purpose.

My Code:

function Lib()
  function self:foo(x, y) return x+y end
  function self:goo(x, y) return x-y end
end
print(Lib():foo(3, 2))

I expect to get 5 but I get the following error.

attempt to index a nil value (global 'self')

What is wrong with my code and how to fix this?

ADDED: Can anyone compare this with using Lib = {} instead? I'm considering to wrap functions with a function since writing self: is easier to maintain than writing Lib. which can be changed later. I wonder if my idea makes sense.


EDITED: Okay, I just found out this works.

function Lib()
  function foo(x, y) return x+y end
  function goo(x, y) return x-y end
  return self
end
Lib()
print(foo(3, 2))

But I don't understand why. Can't functions inside a function be protected?

P.S: I'm sorry if my question is stupid.

3 Answers 3

3

Functions are values that are created when function definitions are executed. Any variable can reference a function value.

So, although a function definition might inside another function body, the function value it not inside the function. It is available via whatever expressions, table keys and fields, and variables that reference it.

Variables are global or local (including parameters). If a name of a variable is not previously declared as local, it is global. So, executing this code:

function Lib()
  function foo(x, y) return x+y end
  function goo(x, y) return x-y end
  return self
end

Sets the global variable Lib to the function value created by evaluating the function definition. That's all up to that point.

Then, executing:

Lib()

Uses the value of the global variable Lib, assumes it's a function and calls it with no parameters. That executes the function, which evaluates a function definition to obtain a function value and sets it to the global variable foo; similarly for goo; and returns the value of the global variable self (presumably nil). The return value is discarded because the call doesn't do anything with it.

Then, executing:

print(foo(3, 2))

Uses the value of the global variable foo, assumes it's a function and calls it with two parameters. That executes the function, which returns 5. It then uses the value of the global variable print, assumes it's a function and calls it with the value 5. The return value is discarded because the call doesn't do anything with it.

Other answers are leading you to what you might want to do. You can evaluate what they are suggesting using these principles.

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

Comments

3
function Lib()
   local function foo(x, y) return x+y end
   local function goo(x, y) return x-y end

   interface = {}
   interface.foo = foo
   interface.goo = goo

   return interface
end

So:

l = Lib()
print(l.foo(3, 2))

will print 5

Is that what you need? There is no direct access to the foo and goo functions.

The next idea this leads to is to not expose, say, the goo function via interface, but just use it within foo or some other function. In that case, goo would be an implementation detail (a private function), not exposed via the object interface.

Comments

3

An implicit self parameter only exists in the body of : method functions.

Since Lib isn't defined with a :, there is no self variable.

You can instead make a factory/constructor which makes a blank instance and attaches the methods:

function new(factor)
    local instance = {factor = factor}

    function instance:multiply(n)
        return self.factor * n
    end

    return instance
end

local m = new(5)
print(m:multiply(6)) --> 3

If you really want a class of which you have many instances, instead of defining a function for each instance, you would attach a metatable to "automatically" attach all of the properties:

local Class = {}

function Class.new(factor)
    local instance = {factor = factor}

    return setmetatable(instance, {__index = Class})
end

function Class:multiply(n)
    return self.factor * n
end

local m = Class.new(5)
print(m:multiply(6)) --> 30

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.