Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Was directory/.pm ever a convention, and why does it exist?

Having just figured out this, I was wondering how the -M would handle a terminal :: affixed to a package name,

$ perl -MFoo:: -e1

What I got was...

Can't locate Foo/.pm in @INC (you may need to install the Foo:: module) (@INC contains: [...]).

This seems to indicate that it doesn't work, so curiosity compelled me....

$ mkdir Foo;
$ echo "package Foo {  sub k { die 42 }  }; 1;" > Foo/.pm
$ PERL5LIB=. perl -MFoo:: -e'Foo::->k'

That did in fact work. Is this convention of use mod::; resolving to mod/.pm supported? Was this a holdover from antiquity. Where did this behavior come from?

With this schema I could do,

Foo/.pm         # package Foo
Foo/Bar/.pm     # package Foo::Bar
Foo/Bar/Baz/.pm # package Foo::Bar::Baz;

then I could do

use Foo::;            # resolves to Foo/.pm
use Foo::Bar::;       # resolves to Foo/Bar/.pm
use Foo::Bar::Baz::;  # resolves to Foo/Bar/Baz/.pm

Foo::->new;
Foo::Bar::->new;
Foo::Bar::Baz::->new;

I like this because this adds the benefit of

  1. Making all of my classes bare words.
  2. Adding consistency between use and invocation.

Is this design by intent? Is it documented anywhere? Has anyone ever thought of this awesome idea before?

like image 924
NO WAR WITH RUSSIA Avatar asked Jan 01 '23 11:01

NO WAR WITH RUSSIA


1 Answers

Is this convention of use mod::; resolving to mod/.pm supported?

No.

The parser is too liberal when it comes to accepting ::, allowing you do rather absurd things.

$ cat Foo/Bar.pm
package Foo::Bar;
sub import { print "imported.\n"; }
print "loaded.\n";
1

$ perl -I. -e'require Foo::Bar'
loaded.

$ perl -I. -e'require Foo::::::Bar'
loaded.

Using the non-canonical form leads to problems.

$ perl -I. -e'use Foo::Bar; use Foo::Bar; use Foo::Bar;'
loaded.
imported.
imported.
imported.

$ perl -I. -e'use Foo::::::Bar; use Foo::::::Bar; use Foo::Bar;'
loaded.
loaded.
imported.

Similarly, that use Foo::Bar:: loads Foo/Bar/.pm shouldn't be taken as an intentional, supported feature.

Has anyone ever thought of this awesome idea before?

It actually would be a mess.

For starters, you would have to use use Foo::Bar::; instead of use Foo::Bar;.

$ cat Foo/Bar/.pm
package Foo::Bar;
sub import { print "imported.\n"; }
sub baz { print "ok.\n"; }
1

$ perl -I. -e'use Foo::Bar;'
Can't locate Foo/Bar.pm in @INC ...

$ perl -I. -e'use Foo::Bar::;'

$

But using use Foo::Bar::; isn't enough because import was never called. The package directive would need to match the file name (package Foo::Bar::) for import to be called, which means you'd have to adjust qualified references to the package as well.

$ cat Foo/Bar/.pm
package Foo::Bar::;
sub import { print "imported.\n"; }
sub baz { print "ok.\n"; }
1

$ perl -I. -e'use strict; use Foo::Bar; Foo::Bar::baz();'
Can't locate Foo/Bar.pm in @INC ...
BEGIN failed--compilation aborted at -e line 1.

$ perl -I. -e'use strict; use Foo::Bar::; Foo::Bar::baz();'
imported.
Undefined subroutine &Foo::Bar::baz called at -e line 1.

$ perl -I. -e'use strict; use Foo::Bar::; Foo::Bar::::baz();'
imported.
ok.

To put it lightly, this is undesirable.

Even if your module doesn't have an import method at this time, that could change, and having two different and incompatible conventions for loading modules is hurtful.


Where did this behavior come from

While the expression Foo::Bar:: produces the string Foo::Bar, use doesn't take an expression.

Additionally, the literal is passed to an implementation of package name to file name transliteration that performs no sanity checks. Perl simply performs an operation equivalent to calling the following sub:

 sub pkg_to_qfn { ( $_[0] =~ s{::}{/}gr ) . '.pm' }
like image 179
ikegami Avatar answered Jan 05 '23 16:01

ikegami