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

Scoping discrepancy between Cc and Lua

Started by LupoCani, 31 January 2015 - 04:18 PM
LupoCani #1
Posted 31 January 2015 - 05:18 PM
I'm currently writing a program that needs to run functions from external files. This in itself is trivial, weren't it for the fact that these functions need read/write access to a number of global variables. I never thought access to global variables could be problematic, however, I have run into some fairly curious scoping rules. After extensive experimentation, I found that this was local to computercraft. Consider the following example:

var = false
print(var)
loadstring("var = true")()
print(var)

When run on my physical PC, this will return false; true. When run on a Cc computer, it will return false; false. The same thing applies to loadfile, loadstring was used for brevity.

Is this an intended/known part of Cc? I've been assuming Cc's Lua implementation to be fairly identical to the original, but I could be wrong. If I'm not, I suppose I've got a bug report to write.
SquidDev #2
Posted 31 January 2015 - 05:49 PM
This is because CC overrides the environment however doesn't set the same environment for loadstring / loadfile. To get around this you can do:

setfenv(loadstring("var = true"), getfenv())()
InDieTasten #3
Posted 31 January 2015 - 05:50 PM
I'm pretty sure this doesn't have to do with computercraft itself, but the library used by computercraft to host lua scripts.
I would assume, that you are running the script with your so called "physical PC" being lua.exe compiled of the "original" C source code downloadable at lua.org
The java version of lua can therefor behave different from it, as it's not "original".

Yeah, it seems that running loadstring creates some sort of environment for the entire chunk. I would be interested in the output of loadstring("print(var)")()
LupoCani #4
Posted 31 January 2015 - 05:57 PM
This is because CC overrides the environment however doesn't set the same environment for loadstring / loadfile. To get around this you can do:

setfenv(loadstring("var = true"), getfenv())()
Interesting. Could you elaborate on how/why that works? I haven't seen those functions before.

Yeah, it seems that running loadstring creates some sort of environment for the entire chunk. I would be interested in the output of loadstring("print(var)")()

I checked that too before posting.
var = true
loadstring("print(var)")()
Will return nil.
var = true
loadstring("var = false")()
loadstring("print(var)")()
print(var)
Will return false; true.
var =nil
loadstring("var = true")()
print(var)
Will, interestingly enough, return true.
Exerro #5
Posted 31 January 2015 - 06:00 PM
Loadstring and loadfile (and dofile?) set (well, leave) the environment as _G, but your script isn't running in _G because it's running in an environment created by os.run() called from shell.run(). Therefore, the global environment of something loaded with loadstring() won't be the same as the global environment of your entire script.

As mentioned above, setting the environment of the script loaded using loadstring() to the current environment will change its global environment to the global environment of the current script rather than _G.



If you don't understand environments, the PIL has a section about them I'm sure.
SquidDev #6
Posted 31 January 2015 - 06:09 PM
Interesting. Could you elaborate on how/why that works? I haven't seen those functions before.
The Programming in Lua book gives a better example, but I will try to explain.

getfenv
This gets the current environment, when you call print (or any global function) the function is looked up in the table that getfenv returns.

setfenv
This sets the environment a function executes in.
The examples you gave in the above post:
Spoiler
I checked that too before posting.
var = true
loadstring("print(var)")()
Will return nil.
.

I'll try to illustrate environments as boxes:

+---------------------+
|  Global environment |
|  +---------------+  |
|  |  Current file |  |
|  |  var = true   |  |
|  +---------------+  |
|  var doesn't exist  |
+---------------------+
var = true
loadstring("var = false")()
loadstring("print(var)")()
print(var)
Will return false; true.

+---------------------+
|  Global environment |
|  +---------------+  |
|  |  Current file |  |
|  |  var = true   |  |
|  +---------------+  |
|  var =false         |
+---------------------+

var =nil
loadstring("var = true")()
print(var)
Will, interestingly enough, return true.


+-------------------------+
|  Global environment     |
|  +--------------------+ |
|  |  var doesn't exist | |
|  +--------------------+ |
|  Var = true             |
+-------------------------+

Var doesn't exist in the child environment as you have set it to nil. This is because setting something a table to nil clears it, so it just looks it up in the parent environment instead.
Edited on 31 January 2015 - 05:10 PM
LupoCani #7
Posted 31 January 2015 - 06:27 PM
Alright, thanks.

Since os.run() allows you to set the environment, could I also circumvent the issue by starting my script using os.run(_G, path)? Or rather, I certainly can, (I just tried) but will it cause other issues? It wouldn't surprise me if a lot of Cc functionality is dependent on it's default environment.
Exerro #8
Posted 31 January 2015 - 07:15 PM
The shell API isn't in _G, so your program wouldn't have access to any of the shell.* functions or variables.
LupoCani #9
Posted 31 January 2015 - 07:28 PM
Alright, I can live with that. Anything else I should be concerned about?