Hi Jamie,
loved your talk at last years Lua workshop!
My question is: Am I asking for trouble by taking this approach?
Apologies for the long post. I did not have enough time to write a shorter one. ;-)
Rosie[1] is an application which exposes a CLI and a C API. The C API, librosie, can be
used from languages like Python, node.js, Ruby, Go, and others[2].
But what about Lua users? Rosie is written in Lua, so I wrote a proper Lua module in a file called 'rosie.lua' . (The 'rosie.lua' file is created from a template when
you install Rosie[3] and build the development branch, tranche-2, because it contains an explicit reference to the Rosie install directory.)
The 'rosie.lua' file can be copied to wherever the user keeps their Lua module files, such as /usr/local/lib/lua/5.3, and it will look in the Rosie installation directory
for everything needed to run Rosie. Thus a single installation of Rosie on a machine will be usable by CLI users, Lua developers, Python developers, etc.
Because Rosie uses C libraries like lpeg.so, 'rosie.lua' loads them from the Rosie installation directory, to ensure that exactly the supported versions are loaded[4].
This also simplifies things for the Lua user, who does not need to install those C libraries in their Lua environment, or who may have different versions of those libraries already installed.
The loading of Lua and C modules is done by a quick hack of a "private module system" that I wrote in an hour. It's literally around 40 lines of code[5] (yay, Lua!).
This quick hack does two things: (1) it loads code from the right place, and (2) it hides all the Rosie internals from the Lua user, including the fact that some C libraries
were loaded.
Why would you hide those modules?
Also: you’re setting a variable `ROSIE_HOME`, in your initial module you could use the output of `debug.getinfo(1)` to dynamically determine where it is located.
When you type 'r = require "rosie"', you get exactly one new global, 'r'. If you look in 'package.loaded', you will see only one new entry (for rosie).
When the user does 'lpeg = require "lpeg"', their own version of lpeg.so is loaded (from wherever they installed it) and everything appears to work fine. Rosie has its
"private" lpeg, and the user has their own.
Is this situation problematic? In what situations will it cause trouble for users?
The only potential problem I see here is the use of 'something global’ by any of the used modules. Examples would be;
- using Lua globals (considered bad practice these days, and easy to check)
- using the Lua registry with named parameters (potential name collisions)
The usual alternative is to make the user install the various C libraries needed by Rosie into their usual Lua lib directory, and for 'rosie.lua' to load them from there.
But this can cause "dependency hell", if the user's needs and Rosie's needs diverge, right?
This relates much to the “why” question above. If you’re targeting regular Lua users with this, then I wouldn’t worry to much about the dependency hell. I’d just make sure it runs with all common Lua versions (if possible 5.1 - 5.3 and LuaJIT) and leave
it there. Possibly publish it on LuaRocks which could deal with the dependencies.
If the intend is packaging in some user application and have Rosie work its magic behind the scenes, then I’d probably use some protection like your hiding of the underlying modules. As in that case a user wouldn’t have a clue probably as to what was wrong
when getting an error. Yet at the same time, in such a scenario a user might not even have the possibility to add extra modules to the setup and hence the problem you’re trying to fix might not even exist there.
hth
Thijs
[4] rosie.lua loads init.lua which contains this line: loadfile(ROSIE_HOME .. "/src/core/load-modules.lua", "t", _ENV)
|