Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module loader #2

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Module loader #2

wants to merge 8 commits into from

Conversation

Qix-
Copy link
Contributor

@Qix- Qix- commented Feb 15, 2019

This adds a module loader subsystem that is compatible with the existing require() functionality but provides a much more convenient and familiar way to manage and locate modules.

The net effects of the environment under terrac with this patch are:

  • require() is a closure that is created every time it is referenced. I explain why below.
  • terralib.modpath is now a path-delimited (; on windows, : on everything else) string of search paths for modules
  • terralib.loadmodule is a handler that is called whenever require() is called. It is backed by a default and sane implementation but can be overridden by scripts if necessary.

Modules are dot-delimited strings that represent a nested module hierarchy. For example, std.thread.mutex would indicate the module mutex that resides within the module thread that resides within the top-level module std.

Module paths that do not begin with a . are absolute module paths. Those that do start with a . are relative module paths.

Absolute module paths are resolved against the current terralib.modpath. The first path to match is considered the resolved path and thus changing the order of paths within terralib.modpath allows the assignation of priority.

Relative module paths are resolved against the current chunk. In the event the chunk is not a file, the loader falls back to the default implementation of require() (or whatever is passed as the third parameter to terra.loadmodule).

Those that wish to override terra.loadmodule can assign a function that accepts the module name (what is passed to require(...)), the originating chunk filepath as an absolute path (optional), and the fallback require() to call in the event the resolution fails (optional).


@aiverson pointed out a very valid caveat to a naïve implementation of this module system, which is handled correctly by this PR's implementation.

This is a slightly modified version of it.

-- main.t
local a = require '.a'

a.run("foo")
-- a/init.t
local b = require '.b'(print)

return {
    run = b
}
-- a/b/init.t
return function(fn)
    local c = require '.c'
    return function(val)
        return fn(c(val))
    end
end
-- a/b/c.t
return function(val)
    return val..val
end

This script correctly outputs foofoo, and require in this case is acting not dissimilarly to Node.js's CommonJS mechanism - that is you say, you could pass require to another script in another directory and that script would be able to resolve modules relative to the originating file's directory.


The practical impact of this is that we can start building out a standardized set of packages using this new scheme. Although it's not incredibly different than Terra's built-in TERRA_PATH variable, this implementation provides adequate control over path composition, giving sane semantics when combining both the environment variable and the command line options.

Let me know if I've missed anything here.

@Qix- Qix- added the enhancement New feature or request label Feb 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant