Seriously ? Ok. A better structured and readable code! Like any other programmation languages.
What is this question ?! Hmm.... ok!
There is some situations where you want to have a standalone program without library, module, or any external dependency. In my case I got this situation :
- On an old game that support lua but only one file to load.
- When I want to make some tool for automated tasks (like shell script but in lua) and you want deploy it by coping the script, no more.
I see to way to define a lua module
Lot of (old) module code use the module
function introduce in (Lua 5.0 ?) Lua 5.1, depreated in Lua 5.2.
The current module definition is done by a return of a single value. This value will be put in cache and return by the require("modulename")
.
Some convention exists
name
,version
,license
,homepage
,description
: ulua_VERSION
,_DESCRIPTION
,_URL
,_LICENSE
: kikito/middleclass_NAME
,_M
,_PACKAGE
: introduce with the deprecated module function ; maybe also the _VERSIONpackage
,version
,source.{url,tag,dir}
,description.{summary,detailed,homepage,maintainer,license}
,dependecies
: luarocks package
but none of them are offical or becomes mandatory.
Usualy, it is a table!
Because :
-
it is easy to define and easy to use.
-
you can store lot of functions inside.
-
file "foo.lua":
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = {
foo = foo,
}
return M
- file "main.lua":
local foo = require "foo"
assert(foo.foo)
print(foo.foo("FOO")) -- print: foo: FOO
- file "foo.lua":
local function foo(self, x)
return "foo: "..tostring(x)
end
return foo
- file "main.lua":
local foo = require "foo"
print(foo("FOO")) -- print: foo: FOO
Like all table in lua we should add some extra handler in his metatable.
The most used is probably the __call
handler.
It is a merge of simple table and function module.
Because it allow you to call the module directly for his main/usual task without to remember the name of the key to use to got the good function, without additionnal existance check of this function.
I think it the most powerfull but also the most complicated to define.
- file "foo.lua":
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = {
foo = foo,
}
setmetatable(M, {__call = foo})
return M
- file "main.lua":
local foo = require "foo"
print(foo("FOO")) -- print: foo: FOO
-- but also
print(foo.foo("FOO")) -- print: foo: FOO
- file "foo.lua":
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = {
foo = foo,
}
setmetatable(M, {__call = foo})
M._VERSION = "0.1.0"
return M
- file "main.lua":
local foo = require "foo"
print(foo._VERSION) -- print: 0.1.0
print(foo("FOO")) -- print: foo: FOO
-- but also
print(foo.foo("FOO")) -- print: foo: FOO
The nil
module returned value is replaced by a boolean true
value.
If a module return true
or false
this value is store.
It is usualy the case when you use an old module that use the deprecated module
function and returns anything.
In some (very rare?) case you should got any other value : userdata
or number
.
Be able to manage them cleanly like package manager :
- (basic) by Name
- (basic) by Version
- by License / Author / URL / ...
Some advanced managment need informations :
- by API : be able to check easily that functions will or will not be available
- by Feature/Behavior : be able to check easily that feature/behavior will or will not be available
All of those points needs to attach information to each module. Some usual current module definition can not support to store more information.
No,Not at all ! We should stop breaking existing module definition !
A returned value and a require
function is enough to build some better stuff and stay compatible with current way to do.
I think so!
I tried to follow the idea : each lua module should be a table with all appropriate fields filled ! But defining all the fields, all the time, for all modules is a pain!
I think we should have a hybride module definition, and automated tool to setup modules...
I started to put name on some told way to define modules.
- single function value : a
micro-module
- single table or callable table value : a
mini-module
- a full filled table (with all version, license, author, ... fields filled) : a
module
- a boolean value : something to fix!
- other value : I don't know for now...
When you (dev) write a module code, stay focused on your needs. I think a new way should :
- argument #1 : something callable
- argument #2 : an table (to access to all content)
- argument #3 : something about custom meta handler
This module is usually the most minimal code possible : return a single function, nothing more.
It is usefull to split the code in lot of part to be able to choose which one is really needed and which one should be dropped.
Main idea: a module definition should focused on the callable stuff.
local function foo(self, x)
return "foo: "..tostring(x)
end
return foo
Return nothing callable, but an additionnal content (a table)
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = { foo = foo }
return M
like a lua module except all meta information, and meta stuff should be done by the module helper
mini-module should only return a table like :
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = { foo = foo }
return foo, M
See the current experimental code : modhelper.lua
Before:
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = { foo = foo }
return M
After:
local function foo(self, x)
return "foo: "..tostring(x)
end
local M = { foo = foo }
return require "modhelper"(M)
In sandbox I'm able to implement a custom require
that integrate a small change.
- catch all the module's returned value
- pass them all to the modhelper
- store the returned result to the
package.loaded
and return it to therequire
caller...
modhelper has a special behavior to support older module definition. if M is a table
return modhelper( M )
equal to
return modhelper( nil, M, M)
- should we attached a
NOP
function to each modules to allow call ? - Find a easy way/convention to keep or drop the first argument for the module
__call
function - What about compat of current module build with metatable ? should we dig into the metatable ?!
- should we add a #4 argument as config to define behavior setting ?
- what about userdata catching ?!
- how to setup a way to easily check if a module is a "new one"
- find a name for my catched module better than "new one" ;)
Support multiple ways to define a module: from the easiest way (because a dev primary think about his code, not how to package it) to the most complexe. Try to got an unified module format when loaded : something callable, something indexable, something with package informations.
modhelper | - | return value | callable | indexable | need setmeta | need getmeta | can have pkg info |
---|---|---|---|---|---|---|---|
without | micro-module | function | yes | no | no | no | no |
without | mini-module 1 | simple table | no | yes | no | no | yes |
without | mini-module 2 | callable table | yes | yes | yes | maybe | yes |
without | - | boolean | no | no | no | no | no |
with | micro-module | function | yes | yes | no | no | yes |
with | mini-module 1 | simple table | yes | yes | no | no | yes |
with | mini-module 2 | callable table | yes | yes | no | no | yes |
with | - | boolean | yes | yes | no | no | yes |
I experiment a new approch to build module.
- split each parts in
micro-module
ormini-module
- Fill the module information (more the one of the project) separatly
- use some util to build the final result!
where/how to store pakcage meta info
My ideal behavior is to be able to load a module (like penligth) require the functions that I really need, and drop (from memory, like unloading) all the useless functions.
The only way I found is to split almost each function, load them on a wrapper, a kind of meta-module ... and setup a way to remove/drop part! TODO: spoke about the reason and take penligth as sample.