lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Am 14.04.2014 11:03 schröbte Enrique Garcia Cota:
Hi there,

Hi!


I've written a series of articles about building Lua modules - it's here:

http://kiki.to/blog/2014/03/30/a-guide-to-building-lua-modules/

I would appreciate any feedback or comments on it.

Rule #1 (Do what Lua does):
One thing Lua does is "provide mechanism not policy"[1]. Yet you are recommending that other people follow your rules. As of your rule #5 you must have good reasons for this, and I'd be very interested in discussing those with you.

  [1]: http://lua-users.org/wiki/MechanismNotPolicy

Rule #3 (Allow monkeypatching):
Lua being a dynamic language doesn't mean that you should monkey-patch (or allow monkey-patching). Lua also is a language that allows you to prevent monkey-patching for certain modules, so it basically comes down to "Allow monkey-patching because I like monkey-patching" which by your own definition isn't a good reason. Regarding the "Beware of locals" part: I think it all depends on the public interface of your module (whether implicit or documented explicitly). In your example I would assume from the function names that `add` adds, and `mult` multiplies. Nothing suggests that monkey-patching `add` should change the behavior of `mult`. This is also exactly "what Lua does", e.g. `table.insert` uses `rawget` and `rawset` internally (well, `lua_rawget` and `lua_rawset`, but the principle is the same). Do you expect `table.insert` to change its behavior if you monkey-patch `rawget`? `print` on the other hand is documented to call `tostring` on its arguments, so you can expect `print` to change its behavior if you redefine `tostring`. In most cases I consider the use of standard library functions in my modules to be an implementation detail, so by caching them in locals I _enable_ you to monkey-patch to your heart's desire without fear of breaking the functions of my modules. And by monkey-patching you usually want to make writing code more convenient. My modules can't profit from that because they are already written ... and tested in a certain environment ... and unaware of your enhancements.

And btw.: locals are faster than upvalues, so for tight loops it might make sense to cache the upvalues defined at the beginning of the module in locals ... :-p

Rule #5 (Beware of multiple files):
If you move your modules from `./my-package` to `./lib/my-package`, you should change `package.path` and not your module references! And your `current_folder` trick is (slightly) broken. When you run `my-package/init.lua` via `require( "my-package" )` `...` will contain "my-package", not "my-package.init", so you must not remove the last module component in this case. If you call `require( "my-package.init" )` however, the extra ".init" is there and must be removed to figure out the package name. You can fix that by explicitly testing for ".init" at the end of the module name.

If you don't use `my-package/init.lua` at all but `my-package.lua` instead, things get much easier: You just use `local mod1 = require( (...)..".module1" )` to get to your submodules. (Again this only matters when you incorporate your module into a larger module hierarchy -- other location changes can be handled with an update to `package.path`.)


Thanks and best regards,

Enrique ( kikito / @otikik )


Philipp