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

Require function

Started by Mister_Y, 03 August 2014 - 08:26 AM
Mister_Y #1
Posted 03 August 2014 - 10:26 AM
Hi everyone!

Because require function is not available, what is the best way to do the same thing?

I have several files (A, B, C …) which declare functions (A.a1, A.a2 in A…) and in each file I need functions in other files (A.a1 can call B.b1), knowing that sometime I redefined functions of another file (in C I can write B.b1 = C.c1).
Finally, I have a main file which load all of this files (And I expect than B.b1 call C.c1 if I load C or I load a file which load C).

Currently I use os.loadAPI, but files are loaded several times so I can not redefined functions (only in the file after the redefinition if I have understood my problem). os.loadAPI is not the good alternative to require function? I do not use it correctly?

Also, I redefined this function to avoid this by register all API already loaded, but if Main need A and B, and A need B, when I execute Main, it load A so B is load by A, and when Main try to load again B nothing happen because B was already loaded by A (expected behavior), but in Main I have no access to B functions. A do not place them in the global variable? What is the right way to do?
Lyqyd #2
Posted 03 August 2014 - 12:39 PM
Let's see the code for these three APIs. I'm not sure why you're trying to do what you say you're trying to do, so it would be helpful to see what you're working with.
Mister_Y #3
Posted 03 August 2014 - 01:46 PM
To clarify the context:
My program starting to grow, I decided to separate it into files. Seeing that I could not use require, in my Main file I used a simple shell.run call to execute other files to include.
But I wanted to use the fact that the function require may return something, so each include files returns a "package" with its own functions:
In A:
local a1 = function()
  -- code...
end

return {
  a1 = a1
}

In Main:
A = require("A")
A.a1()
Therefore, currently in each of its files, I have all my variables and functions in the local scope, and a table in the global scope that contains the public elements with the package name.
In A:
local a1 = function()
  -- code...
end

A = {
  a1 = a1
}

In Main:
shell.run("A")
A.a1()

It works, the concern is that I have to put a comment in each of this files to tell me which another files I need to run before executing this one in my Main file.
And then I read that loadAPI simplify this problem! So I replaced the shell.run by loadAPI (and add unloadAPI at the end of my Main file), and for each of include files I do the same and therefore see directly dependencies.

Unfortunately, if I'm not mistaken, I have the impression that it does not allow function redefinition:
When C load B to redefine that B.b1 now called C.c1, I'm afraid this B.b1 is not the same as the B.b1 available in my Main file.
I'll see if I'm not mistaken by creating a simple example.

By using os.loadAPI

In A:
a1 = function ()
	print("A.a1")
end

In B:
os.loadAPI("A")

b1 = function ()
	print("B.b1")
end

A.a1 = b1

os.unloadAPI("A")

In Main:
os.loadAPI("A")
os.loadAPI("B")

A.a1()

os.unloadAPI("A")
os.unloadAPI("B")

expected:
B.b1
obtained:
main:4: attempt to index ? (a nil value)

By using shell.run

In A:
local a1 = function ()
	print("A.a1")
end

A = {
  a1 = a1
}

In B:
-- must call shell.run("A") in main before

local b1 = function ()
	print("B.b1")
end

A.a1 = b1

B = {}

In Main:
shell.run("A")
shell.run("B")

A.a1()

expected:
B.b1
obtained:
B.b1
Edited on 03 August 2014 - 12:59 PM
TheOddByte #4
Posted 03 August 2014 - 05:30 PM
Why don't you use dofile? :P/>


-- API script or whatever
local t = {}

t.foo = function()
    print( "bar" )
end

return t

-- main file
local t = dofile( "path_to_script" )
t.foo()
Mister_Y #5
Posted 03 August 2014 - 06:34 PM
It works! thank you!

But I have the impression to missing something when I see os.loadAPI.
If I want to get closer to its behavior, the method I use with shell.run function create a global variable as os.loadAPI function, but I can do the same with dofile function.
In that case, why use shell.run?
Lyqyd #6
Posted 03 August 2014 - 06:48 PM
I did mean the actual code, by the way. The "simplified" examples don't tell me anything about why you're trying to do things this way (redefining other API's functions), they just show what you're trying to do. I'm trying to understand why it's necessary so that I can suggest the best way to go about it, or an alternative way of doing it that could take advantage of os.loadAPI.
Mister_Y #7
Posted 03 August 2014 - 07:05 PM
It is for adding behavior to API functions. For example:
I redefine turtle.forward, turtle.up and turtle.down for checking the fuel level of the turtle before moving. If the level is 0, the turtle try to find fuel in its inventory and refuel itself.
So for all programs including my "fuel" file, I do not need to refuel my turtle occasionally, just to have fuel in its inventory.
I do the same for a Positioning system, when the turtle moves it refresh itself its coordinates.

I am interested for good advices on how to program correctly in lua!
Edited on 03 August 2014 - 05:12 PM
Engineer #8
Posted 03 August 2014 - 08:24 PM
It is for adding behavior to API functions. For example:
I redefine turtle.forward, turtle.up and turtle.down for checking the fuel level of the turtle before moving. If the level is 0, the turtle try to find fuel in its inventory and refuel itself.
So for all programs including my "fuel" file, I do not need to refuel my turtle occasionally, just to have fuel in its inventory.
I do the same for a Positioning system, when the turtle moves it refresh itself its coordinates.

I am interested for good advices on how to program correctly in lua!
It is actually very easy to do, first we make hard backup of the turtle table, so we only have function pointers and not table pointes. We can easily do this by doing:

local OldTurtle = {}
for key, value in pairs( turtle ) do
   OldTurtle[ key ] = value
end

Then we can just override the functions we want to:

function turtle.forward()

end

function turtle.back()

end

function turtle.up()

end

function turtle.down()

end
Now if we call turtle.[up/down/back/forward] it will do nothing since we havent programmed it really. Im not going to give you the complete answer but, to actually move up/down/back/forward a turtle, you call the function from our backup. So you get this:

function turtle.forward()
  --# Your logic here
  OldTurtle.forward()
end

function turtle.back()
  --# Your logic here
  OldTurtle.back()
end

function turtle.up()
  --# Your logic here
  OldTurtle.up()
end

function turtle.down()
  --# Your logic here
  OldTurtle.down()
end

That should get you going ;)/>
Mister_Y #9
Posted 03 August 2014 - 09:20 PM
Currently, because I have several files adding behavior at same functions, I have functions which takes a function and a pre- and/or post-function, So I can write:

turtle.forward = utils.callBefore(checkFuel, turtle.forward)
turtle.forward = utils.callAfter(turtle.forward, refreshPosition)

utils.callBefore allow the pre-function to decide if the second function must be called and if not, the return value.
utils.callAfter gives the returns and parameters of the first function to the post-function.

The advices is more for organizing code or things not to do in in Lua. I already program in POO langage (Not to say Java) but I did not really programmed in scripting languages like Lua, Python, Ruby, Javascript…
It is an opportunity :)/>