This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
JokerRH's profile picture

Function enviroments

Started by JokerRH, 15 March 2013 - 09:55 AM
JokerRH #1
Posted 15 March 2013 - 10:55 AM
I'm working on a way to create objects in lua using functions with their own enviroment. (That should mean I create a function that represents my object) It all works great, I can create objects and they are all separate of each other, but I haven't figured out how to extend an object using anotherone.
What I did is I've set a function enviroment and simply added the functions from the other object into it. Unfortiounatly, they do show up if I use getfenv, but they are still bound to their old fenv. That means if I extend a with b and b has the function b.func I can now access it typing a.func, but if a has (native) functions like a.foo I can't access them from a.func
For instance:
a.foo = function() end
b.func = function() foo() end
a.func()
-> error because foo is nil
Is there a way to solve that?
GravityScore #2
Posted 15 March 2013 - 12:48 PM
Use a.foo() instead of just foo().
SuicidalSTDz #3
Posted 15 March 2013 - 01:14 PM
I wonder if anyone knows the definition of foo, as it is a very valid one ^_^/>
JokerRH #4
Posted 15 March 2013 - 07:02 PM
It's not the example, it's because if the way function enviroments work. (so it's not a.foo)
My problem is that if I load my function a, every fubction inside it has the same function enviroment. That means if a.funcA sets name = "Test", a.funcB can refer to that variable, just fine. But the previously inserted functions of b
show up in a, so I can call them, but that
function can't refer to name.
(can't post code yet, I'll have school in a few minutes)
Anyone an idea?
Lyqyd #5
Posted 16 March 2013 - 06:29 AM
Have you tried using an __index metatable entry on the "child" environment table to "inherit" values from the "parent" that aren't present in the "child" environment?
JokerRH #6
Posted 16 March 2013 - 11:01 AM
@Lyqyd: Not exactly. I wrote a little demo program just to show the problem:



function a()
  function f_a1()

  end

  function f_a2(param)

  end

  function test()
	print("This is f_a1 from a.test: "..tostring(f_a1))
	print("This is f_b1 from a.test: "..tostring(f_b1))
  end
end

function b()
  function f_b1()

  end

  function f_b2()

  end
end

term.clear()
term.setCursorPos(1, 1)

--Load function a
objectA = setmetatable({}, {__index = _G})
setfenv(a, objectA)
a()

--Load function b
objectB = setmetatable({}, {__index = _G})
setfenv(b, objectB)
b ()

--Create object c that contains both a and b
objectC = {}
for ind, param in pairs(objectA) do
  objectC[ind] = param
end

for ind, param in pairs(objectB) do
  objectC[ind] = param
end

local func = function() end
setfenv(func, objectC)

--Check the enviroment
print()
print("This is the current enviroment of objectC: ")
if term.isColor then term.setTextColor(colors.green) end
for ind, param in pairs(getfenv(func)) do print(ind) end
if term.isColor then term.setTextColor(colors.white) end

--Trying it
print()
objectC.test()


print()
print("This is the enviroment of f_a1")
if term.isColor then term.setTextColor(colors.green) end
for ind, param in pairs(getfenv(objectC.f_a1)) do print(ind) end
if term.isColor then term.setTextColor(colors.white) end

print()
print("This is the enviroment of f_b1")
if term.isColor then term.setTextColor(colors.green) end
for ind, param in pairs(getfenv(objectC.f_b1)) do print(ind) end
if term.isColor then term.setTextColor(colors.white) end


Theorethically, objectC contains objectA and objectB, as it can be seen in the functionenviroment.
Practically, all the functions of objectA are in objectC, but still have their own functionenviroment
That's why I can't adress any functions through a.test that originally belong to objectB.
Is there a way to get these two tables of functions (objectA and objectB) to share one fenv (objectC)?

Edit: The effect is more obvious with the two loops I added that will print the different fenvs, but I can't figure out how to solve that…:(/>
GopherAtl #7
Posted 16 March 2013 - 11:05 AM
they don't have to. metatables will let you make them behave as if they do. B's metatable is set to {__index=A}, and any calls through B will check B first and, failing to find the key, will go on to check A.
JokerRH #8
Posted 16 March 2013 - 11:08 AM
they don't have to. metatables will let you make them behave as if they do. B's metatable is set to {__index=A}, and any calls through B will check B first and, failing to find the key, will go on to check A.

That wouldn't work, because if 'a' changes a variable 'b' wants to print, it can't refer to it. :(/>
JokerRH #9
Posted 16 March 2013 - 02:21 PM
Solved. It is so simple right as you get the solution, but it may takes a couple of hours…:D/>
For anyone who is interested in it: You just have to set the fenv of both functions to the same table:

--a and b are given functions
--Creating independent functions
newA = a
newB = b

--setting enviroment
local env = setmetatable({}, {__index = _G})
setfenv(newA, env)
setfenv(newB, env)

--load functions
newA()
newB()

--return object
return env
Pharap #10
Posted 16 March 2013 - 04:09 PM
Or as an alternate option, write the function that actually gets assigned to __index when you do

local env = setmetatable({}, {__index = _G})
The CraftOS bios actually replaces the native setmetatable with a version that allows you to seemingly assign tables to the __index of a metatable, when in actuality it's creating a method that searched the table for a value and if it isn't nil returns it. You can use this system to access _G as well as other function's environments by using getfenv to retrieve the function's environment, then making __index search it as a part of its checks, then using setfenv on the other function so it's searching two tables when it tries to index it's environment.

Lua - so much crazy lol