On Sun, Jun 1, 2025 at 3:18 AM Rob Landers <rob@bottled.codes> wrote:
> This could work! I have a couple of critiques, but they aren’t negative: > > I think I like it. It might be worth pointing out that JavaScript "hoists" > the imports to file-level during compilation — even if you have the import > statement buried deep in a function call. Or, at least it used to. I > haven’t kept track of the language that well in the last 10 years, so I > wouldn’t be surprised if it changed; or didn’t. I don’t think this is > something we need to worry about too much here. > As I pointed out in detail to Rob off list JavaScript has 2 import mechanisms that are subtly different from each other. Those interested can read here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import > It’s also worth pointing out that when PHP compiles a file, every file has > either an explicit or implicit return. > https://www.php.net/manual/en/function.include.php#:~:text=Handling%20Returns%3A,from%20included%20files > . > True, but it's a rarely used mechanism and a return statement isn't required - hence an implicit return is possible. > > So, in other words, what is it about require_module that is different from > `require` or `include`? Personally, I would then change PHP from "compile > file" mode when parsing the file to "compile module" mode. From a totally > naive point-of-view, this would cause PHP to: > > 1. if we already have a module from that file; return the module > instead of compiling it again. > 2. swap out symbol tables to the module’s symbol table. > 3. start compiling the given file. > 4. concatenate all files as included/required. > 5. compile the resulting huge file. > 6. switch back to the calling symbol table (which may be another > module). > 7. return the module. > > For a v1, I wouldn’t allow autoloading from inside a module — or any > autoloaded code automatically isn’t considered to be part of the module (it > would be the responsibility of the main program to handle autoloading). > This is probably something that needs to be solved, but I think it would > need a whole new approach to autoloading which should be out of scope for > the module RFC (IMHO). > > In other words, you can simply include/require a module to load the entire > module into your current symbol table; or use require_module to "contain" > it. > Yes, that is certainly possible but comes at an opportunity cost from an engine design standpoint. This is an exceedingly rare opportunity to go back and fix mistakes that have dogged the language for sometime that simply can't be fixed without creating large backwards compatibility problems. For instance, say for the sake of example that PHP files could be compiled 10 times faster if the object parser could assume the whole file was code and there's not going to be any <?php ?> tags or Heredoc or Nowdoc blocks. It might be worth it then to have modules not allow such, and if an author had a block of code that they really wanted this templating behavior to apply to they still could issue a require. Maybe it's time to dig up some downvoted RFC's that got killed for these reasons. > > As for what should a module return? I like your idea of just returning an > object or closure. > I'm leaning towards something akin to a static class. If modules have export keywords (note that if they have their own parser they can also have keywords without disrupting the existing PHP ecosystem) then compiling one would be a static class with members and methods matching what was exported. > I just had another thought; sorry about the back-to-back emails. This > wouldn’t preclude something like composer (or something else) from being > used to handle dependencies, it would just mean that the package manager > might export a "Modules" class + constants — we could also write a composer > plugin that does just this: > require_once 'vendor/autoload.php'; > $module = require_module Vendor\Module::MyModule; > where Vendor\Module is a generated and autoloaded class containing consts > to the path of the exported module. That leads into some thoughts I have on loading modules in general. require_module is the simplest expression. That said, 'use module' might also be appropriate. require_once 'vendor/autoload.php'; use module Vendor/Module as MyModule; Something like that? JavaScript has a distinction between global scope import, which is almost analogous to our namespaces and use statement, and dynamic scope import, which is almost analogous to our require statements. Don't know if it's useful to us to draw such a distinction.