Lua - How do I dynamically call a module? - oop

Here's some much-simiplified Lua code I'm working with. I need to know how to dynamically call another module ('zebra'):
avar = require "avar"
bvar = require "bvar"
function create(zebra)
print(zebra.new())
end
print(create(avar))
And here are two modules:
local Avar = {}
function Avar.new()
return "avar"
end
return Avar
local Bvar = {}
function Bvar.new()
return "new"
end
function Bvar.old()
return "old"
end
return Bvar
If I try to pass in the string "avar" to my 'create' function, it doesn't work. If I pass in the word 'avar' with no quotes, it does work, however, I don't understand what avar with no quotes is? It seems to be a blank table? Not sure how to pass a blank table as an argument in my main program.
But maybe I'm totally on the wrong path. How do I dynamically call modules?

You can require any time:
function create(zebraModuleName)
zebraType = require(zebraModuleName)
print(zebraType .new())
end
print(create("avar"))
print(create("bvar"))

avar without the quotes is a global variable you created. It is initialized to the value returned by the require function1, which is the value returned by the module you are invoking. In this case, its a table with the new field that happens to be a function.
1 Importing a modules in Lua is done via regular functions instead of a special syntax. The function call parenthesis can be ommited because parens are optional if you write a function call with a single argument and that argument is a string or a table.
Other than that, there are also some other things you are confusing here:
The table you are storing on avar is not empty! You can print its contents by doing for k,v in pairs(avar) do print(k,v) end to see that.
The avar, bvar and create variables are global by default and will be seen by other modules. Most of the time you would rather make them local instead.
local avar = -- ...
local bvar = -- ...
local function create (zebra)
-- ...
end
The create function clearly expects a table since it does table indexing on its argument (getting the new key and calling it). The string doesn't have a "new" key so it won't work.
You aren't really dynamically calling a module. You are requiring that module in a regular way and it just happens that you pass the module return value into a function.
create always returns nil so there is no point in doing print(create(avar)). You probablu want to modify create to return its object instead of printing it.

You can use standard require from lua language or build your own loader using metatables/metamethods.
1. create a global function:
function dynrequire (module)
return setmetatable ({},{
__index = function (table,key)
return require(module..'.'..key)
end
})
end
2. Create your project tree visible to package.path
./MySwiss/
\___ init.lua
\___ cut.lua
\___ glue.lua
\___ dosomething.lua
3. Make your module dynamic
you only need to put this line on your MySwiss/init.lua (as if you were namespacing a PHP class):
return dynrequire('MySwiss')
4. Require your module and use subproperties dynamically
On your script you only need to require MySwiss, and the folder file (or subfolders with dynrequire('MySwiss.SubFolderName').
var X = require('MySwiss')
X.glue()
Note that MySwiss doesn't have glue key. But when you try access de glue key the metamethod __index try to require submodule. You can the full project tree using this technique. The only downside is the external dependencies not packed this way.

Related

Get list of variables in the current namespace in Julia

I have a question that is similar to, but different than this one. I have a function within a module, and I would like to see what variables are defined inside the function namespace. In the other post, they said to use varinfo, but that seems to only work in the Main namespace. For example if I run this
module Test
function hello()
a = 1
varinfo()
return a
end
end
import .Test
Test.hello()
I get this error
WARNING: replacing module Test.
ERROR: UndefVarError: varinfo not defined
Is there a way to get a list of variables within a given namespace? What I am looking for is a function that when called, outputs all the available variables (a in my example) as well as available modules within the namespace.
PS. I would like to add, that varinfo is incredibly limiting because its output is a Markdown.MD, which cannot be iterated over. I would prefer a function that outputs variables and values in some sort of list or dictionary if possible.
varinfo is showing only the global variables.
If you want the local variables, you need to use the Base.#locals macro:
module Test
function hello()
a = 1
println(Base.#locals)
return a
end
end
And now you can do:
julia> Test.hello()
Dict{Symbol, Any}(:a => 1)
1
Is this what you want?
module Test
function hello()
a = 1
println(Main.varinfo(#__MODULE__; all=true, imported=true))
return a
end
end

Inherit from table returned from function

There is an API provided function, let's call it createBase which returns a table (object). I want to add methods to this table, but I can't just do x = createBase() and then function x:foo() because I have another function similar to createBase, but it's createExtended. It might be easier to explain with the code I have so far:
import api --I don't know how you'd do this in vanilla Lua, I'd use os.loadAPI("api") but that's computercraft specific, I think
Extended = {}
function Extended:foo()
print("foo from extended")
end
function createExtended(params)
x = api.createBase(params)
Extended.__index = x
return Extended --this is obviously wrong: you can't return a class and expect it to be an object
end
Of course, this doesn't work: but I don't know how I might make it work either. Let's assume the table returned by createBase has a function called bar which just prints bar from base. With this test code, the following outputs are given:
e = createExtended()
e.foo() --prints "foo from extended"
e.bar() --nil, therefor error
How can I make this possible, short of defining function x.bar() inside createExtended?
Thanks in advance.
The very simplest way is to attach the method to it directly, instead of using a metatable.
local function extend(super_instance)
super_instance.newMethod = newMethod
return super_instance
end
local function createExtended(...)
return extend(createSuper(...))
end
This will work, unless your superclass uses __newindex (for example, preventing you from writing to unknown properties/methods), or iterates over the keys using pairs or next, since it will now have an additional key.
If for some reason you cannot modify the object, you will instead have to 'wrap' it up.
You could make a new instance which "proxies" all of its methods, properties, and operators to another instance, except that it adds additional fields and methods.
local function extend(super_instance)
local extended_instance = {newMethod = newMethod}
-- and also `__add`, `__mul`, etc as needed
return setmetatable(extended_instance, {__index = super_instance, __newindex = super_instance})
end
local function createExtended(...)
return extend(createSuper(...))
end
This will work for simple classes, but won't work for all uses:
Table iteration like pairs and next won't find the keys from the original table, since they're not actually there. If the superclass inspects the metatable of the object it is given (or if the superclass is actually a userdata), it will also not work, since you'll find the extension metatable instead.
However, many pure-Lua classes will not do those things, so this is still a fairly simple approach that will probably work for you.
You could also do something similar to Go; instead of having a way to 'extend' a class, you simply embed that class as a field and offer convenience to directly calling methods on the wrapping class that just call the methods on the 'extended' class.
This is slightly complicated by how 'methods' work in Lua. You can't tell if a property is a function-that-is-a-property or if it's actually a method. The code below assumes that all of the properties with type(v) == "function" are actually methods, which will usually be true, but may not actually be for your specific case.
In the worst case, you could just manually maintain the list of methods/properties you want to 'proxy', but depending on how many classes you need to proxy and how many properties they have, that could become unwieldy.
local function extend(super_instance)
return setmetatable({
newMethod = newMethod, -- also could be provided via a more complicated __index
}, {
__index = function(self, k)
-- Proxy everything but `newMethod` to `super_instance`.
local super_field = super_instance[k]
if type(super_field) == "function" then
-- Assume the access is for getting a method, since it's a function.
return function(self2, ...)
assert(self == self2) -- assume it's being called like a method
return super_field(super_instance, ...)
end
end
return super_field
end,
-- similar __newindex and __add, etc. if necessary
})
end
local function createExtended(...)
return extend(createSuper(...))
end

unable to use imported modules in event_handler lua

I'm writting a basic event handler in lua which uses some code located in another module
require "caves"
script.on_event({defines.events.on_player_dropped_item}, function(e)
caves.init_layer(game)
player = game.players[e.player_index]
caves.move_down(player)
end
)
but whenever the event is triggered i get following error
attempt to index global 'caves' (a nil value)
why is this and how do i solve it?
You open up the module in question and see what it exports (which global variables are assigned and which locals are returned in the bottom of the file). Or pester the mod author to create interface.
Lua require(filename) only looks up a file filename.lua and runs it, which stands for module initialization. If anything is returned by running the file, it is assigned into lua's (not-so) hidden table (might as well say, a cache of the require function), if nothing is returned but there was no errors, the boolean true is assigned to that table to indicate that filename.lua has already been loaded before. The same true is returned to the variable to the left of equals in the caves = require('caves').
Anything else is left up to author's conscience.
If inside the module file functions are written like this (two variants shown):
init_layer = function(game)
%do smth
end
function move_down(player)
%do smth
end
then after call to require these functions are in your global environment, overwriting your variables with same names.
If they are like this:
local init_layer = function(game)
%do smth
end
local function move_down(player)
%do smth
end
then you won't get them from outside.
Your code expects that the module is written as:
caves = {
init_layer = function(game)
%do smth
end
}
caves.move_down=function(player)
%do smth
end
This is the old way of doing modules, it is currently moved away, but not forbidden. Many massive libraries like torch still use it because you'd end up assigning them to the same named globals anyway.
Кирилл's answer is relevant to newer style:
local caves={
%same as above
}
%same as above
return caves
We here cannot know more about this. The rest is up to you, lua scripts are de-facto open-source anyways.
Addendum: The event_handler is not part of lua language, it is something provided by your host program in which lua is embedded and the corresponding tag is redundant.
You should consult your software documentation on what script.on_event does in this particular case it is likely does not matter, but in general the function that takes another function as argument can dump it to string and then try to load it and run in the different environment without the upvalues and globals that the latter may reference.
require() does not create global table automatically, it returns module value to where you call this function. To access module via global variable, you should assign it manually:
local caves = require "caves"

lua modules - what's the difference between using ":" and "." when defining functions? [duplicate]

This question already has answers here:
Difference between . and : in Lua
(3 answers)
Closed 8 years ago.
I'm still playing around with lua modules and I've found the following "interesting" issue that occurs depending on how you create your methods / functions inside a module.
Note the following code in a file called test_suite.lua:
local mtests = {} -- public interface
function mtests:create_widget(arg1)
print(arg1)
-- does something
assert(condition)
print("TEST PASSED")
end
return mtests
Using the above code, arg1 is always nil, no matter what I pass in when calling create_widget(). However, if I change the definition of the function to look like this:
function mtests.create_widget(arg1) -- notice the period instead of colon
print(arg1)
-- does something
assert(condition)
print("TEST PASSED")
end
then, the system displays arg1 properly.
This is how I call the method:
execute_test.lua
local x = require "test_suite"
x.create_widget(widgetname)
Can you tell me what the difference is? I've been reading: http://lua-users.org/wiki/ModuleDefinition
But I haven't come across anything that explains this to me.
Thanks.
All a colon does in a function declaration is add an implicit self argument. It's just a bit of syntactic sugar.
So if you're calling this with (assuming you assign the mtests table to foo), foo.create_widget(bar), then bar is actually assigned to self, and arg1 is left unassigned, and hence nil.
foo = {}
function foo:bar(arg)
print(self)
print(arg)
end
Calling it as foo.bar("Hello") prints this:
Hello
nil
However, calling it as foo:bar("Hello") or foo.bar(foo, "Hello") gives you this:
table: 0xDEADBEEF (some hex identifier)
Hello
It's basically the difference between static and member methods in a language like Java, C#, C++, etc.
Using : is more or less like using a this or self reference, and your object (table) does not have a arg1 defined on it (as something like a member). On the other way, using . is just like defining a function or method that is part of the table (maybe a static view if you wish) and then it uses the arg1 that was defined on it.
. defines a static method / member, a static lib, Which means you can't create a new object of it. static methods / libs are just for having some customized functions like printing or download files from the web, clearing memory and...
: Is used for object members, members that are not static. These members change something in an object, for example clearing a specified textbox, deleting an object and...
Metamethod functions(Functions that have :) can be made in lua tables or C/C++ Bindings. a metamethod function is equal to something like this on a non-static object:
function meta:Print()
self:Remove()
end
function meta.Print(self)
self:Remove()
end
Also, with . you can get a number/value that doesn't require any call from a non-static or static object. For example:
-- C:
int a = 0;
-- Lua:
print(ent.a)
-- C:
int a()
{
return 0;
}
-- Lua:
print(ent:a())
same function on a static member would be:
print(entlib.a())
Basically, each non-static object that has a function that can be called will be converted to : for better use.

return type of who_user in scilab

I work with scilab, but during a project, scilab has to deal with a large number of variables.
I was wondering if i can do the following
var_list = who_user();
for _var_ = var_list do
if _var_ is global then
writetofile(human_readablefile, _var_)
end
end
clear()
of course this is a pseudocode, and i have a few questions before i implement it.
I can not get var_list = who_user() working. so i believe the function does not return anything. I am reluctant to hack into the code of the "who_user" macro itself. Is there any other way to get the list of user variables in another variable?
Is there a way to find the global variables out of them?
If not, then what are some memory management techniques in scilab?
I am able to answer your first query:
From a slight modification of the who_user function itself:
function nams = who_user1()
//get user variables
[nams,mem]=who('get'); //get all variables
p=predef(); //number of system variable
st=stacksize()
nams=nams(1:$-p+1);mem=mem(1:$-p+1);
//modifiable system variables
excluded=['demolist','scicos_pal','%scicos_menu',..
'%scicos_short','%helps','%helps_modules','MSDOS','who_user','%scicos_display_mode', ...
'%scicos_help'];
ke=grep(nams,excluded)
nams(ke)=[];mem(ke)=[];
n=size(nams,1);
if n==0 then return,end
//format names on n*10 characters
ll=length(nams)+2;m=int((ll-1)/10)+1;
for k=1:max(m)
ks=find(m==k);
if ks<>[] then nams(ks)=part(nams(ks),1:(k*10));end
end
endfunction
This function should give you the list you desire (I have modified the name to who_user1).
You can find out whether a specific variable is global or not by using the isglobal() function, but you need to pass a variable to isglobal(), not the string that is the name of the variable. The function I've listed above returns a vector of strings.
An alternative approach you could try would be to rewrite the above function to return the variables (rather than their names) directly using varargout and then testing them for being globals.