On Sun, May 4, 2025, at 5:40 PM, Michael Morris wrote:
> On Sun, May 4, 2025 at 5:38 PM Larry Garfield <[email protected]> wrote:
>>
>> > PHP Code -----------------------------------------------------------------------
>> >
>> > namespace MyModule;
>> >
>> > yield function sum(a, b) { return a + b; }
>> >
>> > --------------------------------------------------------------------------------
>>
>> Every module author changes their code. (Which is reasonable.)
>
> Actually no. PHP Modules are only needed if the package author wants
> the functionality modules provide - specifically having an independent
> symbol table and autoload queue. Existing packages don't have to change
> in any way. If a module includes one their symbols will be written
> into that module's symbol table. If two modules include the same
> package they each write it onto their own respective symbol tables.
I thought it self-evident that I was referring to "every package author that wants to expose
their package as a module", not literally every owner of a package on Packagist. (Though
presumably over time those two circles will increasingly overlap.) But let me make that explicit.
And, as noted, requiring the author of a package to introduce new syntax in order to turn the
package into a module is a reasonable expectation. I have no issue with that.
>> > PHP Code -----------------------------------------------------------------------
>> >
>> > use require 'mymodule.php';
>> >
>> > echo MyModule::sum(3, 4);
>> > --------------------------------------------------------------------------------
>>
>> Every caller changes their code, to use use require
and to use the
>> pseudo-class syntax to call the function.
>
> Again, if they want module functionality. No one *has* to do this - the
> new syntax was carefully chosen to let the existing syntax work as it
> always has.
If my library is structured as a module, with yield and stuff, then how exactly would someone use it
without using the use require
syntax at least?
>> > PHP Code -----------------------------------------------------------------------
>> >
>> > use sum require 'mymodule.php';
>> >
>> > echo sum(3, 4); // 7
>> > --------------------------------------------------------------------------------
>>
>> Removes the pseudo-class syntax, but still has the "use sum require...." change
>> for the consumer.
>>
>> As described, the implication is that the millions of existing
>>
>> use Some\Class\Here;
>>
>> lines in code in the wild will... not work if the Some\Class package becomes a module?
>
> This is left up to the autoloader. When you call that use statement
> from the main thread the autoloader receives args ("Some\Class\Here",
> false). When you call that use statement from a module the autoloader
> receives arguments ("Some\Class\Here", "RequestingModule"). If the
> autoloader isn't rewritten it will ignore the 2nd argument and serve
> out the same code for both module and main thread.
I believe you are grossly misusing the term "thread" here. There is no separate execution
thread involved, just scoped symbol tables.
See note further down about the rest of the points raised here.
> HOWEVER, modules can define their own autoloaders and those will be
> executed independent of the autoloader on the main thread. Furthermore,
> the autoloader on the main thread will not be used unless the module
> opts into using it.
Nothing in your initial post discussed any of this, or even implied it.
>> I think that's what's implied, but it's not clear. If that triggers
>> autoloading, how does the autoloader know how to find it? It needs to know the module name, which
>> as described is the file name of the module, not the class.
>
> By convention the filename might be the module name, but that a
> convention of package management, autoloading, some future PSR-X that
> is far, far outside of scope here. The module's name is its namespace!
> For a module a namespace isn't just a run time string replacement, it
> has a very real effect. The namespace will be the 2nd argument of every
> autoload call made from the module or any file required/included in its
> scope. Existing packages don't need to be aware this is happening -
> but the module can have a custom autoloader that always loads version 2
> of a popular package even if composer on the main thread is set to
> include version 3.
From the proposal:
> use require 'mymodule.php';
>
> echo MyModule::sum(3, 4);
How exactly does MyModule get defined? That is not at all clear.
>> Since the module name is the file name, that means if a module has 50 internal classes, it
>> must be in the same file. Hence, giant files.
>
> I hope you can see why this isn't true now. The module entry file can
> simply yield out its public assets like so.
>
> namespace Vendor\Package;
> require 'autoloader.php';
> yield { A, B, C }
... Nothing in your initial proposal suggested that in the slightest. It did not even include yield
with brackets to list multiple options.
> The above assuming that the autoloader knows where \Vendor\Package\A,
> \Vendor\Package\B and \Vendor\Package\C are located at. If an
> autoloader isn't used they'll have to be explicitly required before
> being yielded out.
>
>>
>> If those reads of your post are inaccurate, then please show with examples how they are
>> inaccurate, because that is how I understood your proposal text at face value.
>>
>
> Ok, let's go over them in turn
>
>>> 1. Every module author to change their coding structure.
>
> No. Modules aren't replacing the current mechanism - just augmenting it
> when needed. The new syntax is only necessary when an independent
> symbol table is desired. Further, there's nothing stopping a package
> from having a standard require entry file that doesn't set up the
> symbol table and related protections, and one that does.
See above.
>>> 2. Every consumer of a package to change their coding structure
>
> That depends on the package maintainer. They can certainly rewrite the
> package such that consuming code must use module syntax to access it,
> but this isn't required of them. And as mentioned above they can get
> clever and provide multiple entry points to their code if they desire
> just as JavaScript package devs have been doing to make sure their
> packages work whether the consumer uses ESM import or CommonJS import.
>
>>> 3. Devs to abandon "it just works" autoloading and explicitly import
>>> packages.
>
> I don't know where you got that from - maybe the old thread from 9
> months ago? The proposal I made last night not only takes autoloading
> into account but also discusses how autoload functions will receive a
> second argument moving forward that lets them make decisions about what
> code to supply to modules based on whatever logic they have which is
> well beyond the scope of this proposal.
It came from the examples in the original proposal at the top of this thread:
> use sum require 'mymodule.php';
>
> echo sum(3, 4); // 7
So if I want to call sum(), I have to explicitly use sum require
it. I cannot just let
the autoloader sort it out. (Assuming we were talking about a class or function autoloading ever
happens, of course.)
>>> 4. Abandoning 16 years of PSR-0/4 file convention in favor of "module =
>>> file", which will almost certainly result in multi-thousand-line files even for clean,
>>> well-factored code.
>
> Again, I don't see this, at all. And I'm a little insulted you'd think
> that I'm dumb enough to propose something that outrageous. Modules
> simply have their own symbol table scope. They can import any package
> as they exist today without any modification into that scope. They can
> therefore pull a different version of the same package, which is
> desirable if they have a compatibility issue with the latest version of
> the package. Those autoloader are important, but out of scope.
The implication from your original post is that if I want to import 3 symbols from a module, I
require a single file. That file has one or more symbols that are yield
ed.
Implication: If I want 3 symbols in a module together, they have to be in the same file.
Your response here about having a fronting file that yields symbols it doesn't define, on the
assumption that an autoloader will figure it out in the backend, was never stated nor implied
anywhere in your proposal. Rather, it states that modules look like this:
> yield function sum(a, b) { return a + b; }
IE, like Javascript or Python libraries. That is the plain reading of your original post. And
naturally means that whoever is authoring that file needs to make changes to it, to at minimum add
the "yield" keyword.
So before you get indignant and call me a liar ("You're 4 for 4 on falsehoods"),
perhaps consider how someone you know is "more intelligent" than this can come away from
your post understanding it so utterly differently than what you intended.
From your follow up statements, it seems that what you are proposing is not "modules" in
any sense used in any language I am familiar with. Rather, you're proposing a symbol table
hack to get around PHP's inability to load two symbols with the same name, and using module
terminology to inaccurately describe it. (And thread terminology as well.) That is, it seems your
intent is that I can import arbitrary code from an arbitrary package and shunt it off to a separate
symbol table. But it's not clear if you intend for the package author to have to do anything
to enable that. Your answers seem contradictory.
For a concrete example, I'll offer my AttributeUtils library: https://github.com/Crell/AttributeUtils/tree/master/src
It has a whole bunch of interfaces, plus a few classes. We'll focus on a subset of them for
the moment.
Analyzer.php (this is the main class someone uses)
AttributeParser.php (this is an internal class used by Analyzer, you should never use it directly)
FromReflectionClass.php (an interface that other libraries implement to trigger certain
functionality in Analyzer)
Say I want to modularize this library, under your proposal. What would I, as the library author, do
exactly? Anything? How can I mark AttributeParser as internal-only and
thou-shalt-not-use-if-you're-not-me? (That's the most common value of module systems, and
the one most people in PHP seem to think of, from what I've seen.)
That library is used extensively by Serde (another of my libraries). If I modularize
AttributeUtils, then what changes does Serde need to make? What would it get out of doing so?
Code examples, please.
--Larry Garfield