Conventional - yes. Shared - yes. Non-global - I’m not sure what how to interpret that. Certainly require is bringing in code which is not part of the built-in/default environment. The require function is part of the Lua language.
Based on the stack trace from the failed require call it does appear that my earlier assertion that require does not search directories under ~/dust/code is in fact not true - it does look like the scripts are looked for directly under ~/dust/code but not any of its subdirectories.
Looking over the norns documentation I see that there isn’t really a ton of guidance as to when to use require and when to use include (something I will raise with the dev team). What would probably be of the most value to people is highlighting the two areas where the behavior of require and include differ, specifically (a) where do they search for lua files to load and (b) what happens if the same lua module is required/included multiple times.
Where do they search?
-
require: looks for a file along the configured Lua module search path
-
include: looks for a file in one of two places; relative to the currently loaded/running script or relative to the top of the ~/dust/code directory tree (the code for reference)
If one is trying to access lua code from files which lives within the ~dust/code (basically anything editable within maiden) the include is your friend.
If one is trying to access lua code which is part of the norns/matron software itself (such as these files) then require is what you what.
What happens when the same lua code is referenced twice?
-
require: the first time a lua module is required it is loaded up and the module is added to a list of currently loaded modules. Should a file be require-ed a second time the previously loaded module is returned - it is not loaded a second time.
-
include: the file is loaded fresh again from the filesystem each time the include function is called.
The main benefit of include here is that if one is working on building a library of code for use in a script, each time the script is run any changes to the file being include-ed will picked up. If we used require then we would have to manually remove the previous copy from the list of loaded modules any time we wanted to pick up changes to the library module.
One unfortunate characteristic of Lua is that all variable (name bindings) are global by default unless one explicitly declares them local. The net effect is that it is very easy for modules to produce global side-effects when they are loaded. As of today the norns menu system and each script as it is loaded share the same Lua interpreter environment and there isn’t much if any sandboxing of scripts/modules as they are loaded. There has been a healthy amount of debate around trying to take steps to better sandbox scripts and to remove any side-effects produced when the script is run.
Under the hood include and require (to my knowledge) use the lower level Lua function dofile to read/load Lua code so I believe they are more or less the same in not providing any sandboxing.
Hopefully that helps clear up some of the confusion (instead of creating more)!