Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Making it clearer to say that a sub is defined within current package

by bliako (Priest)
on Apr 11, 2019 at 14:24 UTC ( #1232441=perlquestion: print w/replies, xml ) Need Help??
bliako has asked for the wisdom of the Perl Monks concerning the following question:

package X::Y::Z; sub xyz { ... } sub abc { X::Y::Z::xyz(); # I have just called xyz() from this current package # by using the above expression, except from making sure I don't a +ccidentally call another xyz(), # my intent is to say: # "reader: don't go looking for xyz() elsewhere, it is declared +in this same package" # but the above is too long and some reader may forget # what package there editor is currently showing or confuse it wit +h similarly named: X::Y::Z1 etc. # I would prefer a shorter but equivalent way of calling xyz() of +current package # similar to ::xyz() for main - short and to the point: this sub i +s implemented IN THIS package }

I would like to show to the reader of my programs where this variable or that function is declared/defined in. Especially when that reader is me later on. I would hate to save stack and go to cpan or my local disk to search for this function's implementation. Just to notice later that it is further below in same file in my editor.

So, I usually use the fully-qualified name for subs that I am using, like in the above code X::Y::Z::xyz();. But that's a bit awkward for me and the reader especially for long package names. So, I thought perhaps there is another way in Perl to write xyz() to mean in this package, in order to show to the reader (and perl) that this function's source code is just below or above this line and should not open another window to search for it.

For example, when in main package, we refer to subs within this package either as xyz() or main::xyz() (which is what I want to avoid: too short/too long). But then is the cute little ::xyz(). The reader gets the message immediately, that xyz() is further below or above current line. Any ideas for the other packages?

thanks, bliako

Replies are listed 'Best First'.
Re: Making it clearer to say that a sub is defined within current package
by haukex (Canon) on Apr 11, 2019 at 17:10 UTC
    But then is the cute little ::xyz(). ... Any ideas for the other packages?

    Yes: xyz()

    ;-)

    So, I thought perhaps there is another way in Perl to write xyz() to mean in this package, in order to show to the reader (and perl) that this function's source code is just below or above this line and should not open another window to search for it.

    A plain xyz() always means "in this package".

    Remember than when importing, for example, Other::Package::foo() into the current package This::Package, you're creating a symbol table entry This::Package::foo that just points to the original foo(). But to the code in This::Package, the symbol exists in the local This::Package symbol table!

    Based on your description, I think maybe you want to differentiate between subs that come from other packages and the ones actually defined in the current package? The two typical ways to do this would be to not actually import anything (use Other::Package ();) and then call the functions explicitly (Other::Package::foo()), or an alternative would be to always list all functions imported from other packages in the use (i.e. use Other::Package qw/foo/; instead of use Other::Package;), because that would allow doing a quick search for "foo" and finding it right at the top of the file, telling the reader where it came from.

    Your idea of calling subs that are defined in the current package with some kind of prefix, I would probably find kind of confusing when reading the code, because package prefixes are in my experience more commonly used when the sub comes from a different package than the current one.

Re: Making it clearer to say that a sub is defined within current package
by LanX (Archbishop) on Apr 11, 2019 at 15:23 UTC
    Like I told you in the CB your "approach" doesn't even work.

    Importing means aliasing into the current package, hence any function will be available.

    Your approach is quite inside out, it's common to import explicitly, hence use module qw/one two three/ makes it obvious, even if module is badly designed and exports one two and three by default.

    There is also the convention to mark private methods with a leading underscore, so _xyz() would be obvious for the reader.

    In order to enforce privacy you can also use private code refs like my $xyz=sub {} and dereference every call. It's not (easily) possible to import private variables so this is safe. There is AFAIK also an experimental feature to allow private sub declarations.

    If all of this is not enough ... Sigh.... you could hack yourself a solution, by aliasing the "local" subs to another temporary package like "_" and call _::xyz(). This export must happen at compile time and prior to any call.

    IIRC it's possible to use introspection to determine where a sub was declared, hence you could even automate this aliasing in a module use _ where the importer inspects the caller's stash.

    Brain fucked and you'll probably sabotage OOP inheritance but hey it's Perl and TIMTOWTDI... =)

    Update

    Just saw Eily's post and his "My::" seems to correspond to my "_::" . Using AUTOLOAD is another option but IMHO this would come with a performance penalty.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Hu, wish I had read what you said before starting posting. I realized the issue with imported functions *after* writing most of it so I added the issue as an afterthought. There's still the option of looking inside @EXPORT lists to only create the aliases for the correct functions though :)

Re: Making it clearer to say that a sub is defined within current package
by roboticus (Chancellor) on Apr 11, 2019 at 15:04 UTC

    bliako:

    I'm pretty sure I understand what you're saying, but I'm not seeing what the problem is.

    If you're just talking about subroutines in packages, then calling xyz() while you're in X::Y::Z won't accidentally call ::xyz() in the event that X::Y::Z::xyz() isn't defined. Instead you'll get an error about the missing subroutine:

    $ cat pm_1232441.pl use strict; use warnings; sub xyz { print "I'm ::xyz()\n"; } { package FOO; sub xyz { print "I'm FOO::xyz()\n"; } sub abc { print "I'm FOO::abc()\n"; xyz(); } } { package BAR; sub abc { print "I'm BAR::abc()\n"; xyz(); } } xyz(); FOO::xyz(); FOO::abc(); BAR::abc(); Roboticus@Waubli ~ $ perl pm_1232441.pl I'm ::xyz() I'm FOO::xyz() I'm FOO::abc() I'm FOO::xyz() I'm BAR::abc() Undefined subroutine &BAR::xyz called at pm_1232441.pl line 21.

    If you're talking about object-oriented programming and you're using inheritance, then if someone overrides your xyz() you'd want to let it get called. Calling a specific implementation of an xyz() is usually an error because the wrong specialization won't know about any potential extra constraints on your object.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Making it clearer to say that a sub is defined within current package
by Eily (Prior) on Apr 11, 2019 at 15:20 UTC

    Edit: a disclaimer first. I think there is some theorical interest in the question, but it's actual realization is not risk-free (either because it somehow bypasses strictures, or because of performance, if not both). That's the main reason why I don't provide a full working example (also because I'm lazy, and my laziness is very happy with that first excuse :P).

    It's complicated. Basically, it's because perl lets you scope those variables dynamically but nor lexically. Or, in simpler English, you can tell how long you want a name to exist, but not where. For example if DWIM() made all the local function available in my::, and unDWIM the reverse you would have:

    package MySelf; my $var='Hello'; sub world { 'World' }; DWIM(); my::world(); # Calls the World function from this package NotMe::planet(); # Sees my::world but not $var unDWIM();
    planet() from NotMe would not be able to access $var, but would be able to call my::world(). And, if there already was a world() function inside NotMe, calling my::world inside NotMe::planet() would call MySelf::world() instead of NotMe::world() as expected...

    I see two (and a half) workarounds for this issue:

    1) You can have a copy of each function in the current package with a name pattern that does what you want. Eg, add my__ before every local function. For example you'd declare: sub world { 'World' }; and call it as my__world from inside your package. Technically that second name would still be available to outside modules as MySelf::my__world, but you could just decide to never call my__ functions with a package prefix (or die by checking if caller is different from the current package). The my__ functions could either be created from the list of @EXPORTs, or by using AUTOLOAD to create the alias *{"my__$function"} = \&$function when you need it.

    2) Also using AUTOLOAD, you can have a package My; so that when you call My::anyFunction from a package, it would look inside the calling package for "anyFunction" and call it if it exists.

    Although the two AUTOLOADsolutions look more convenient, they would be tricked by imported functions. So if you import Data::Dumper into MySelf, my__Dumper or My::Dumper would both call Data::Dumper::Dumper as if it had been defined inside MySelf. So I suppose the better solution would be to create an alias for every function that you export?

    There also the alternative of doing it the other way around: never importing function in the current package, for example:

    package MySelf; package Ext { use Data::Dumper; use Text::CSV qw( csv ); }; Ext::Dumper(Ext::csv(in => "data.csv"));
    The issue with that one is that you would be able to call Ext::csv from any other package, even if it doesn't have the matching use in its file.

      > My::Dumper() would both call Data::Dumper::Dumper()

      It's definitely possible to tell in which package a sub was declared.

      I saw it in the book perlhacks and IIRC it's available in B

      The problem I see is that My:: is global, you would need the hint hash to restrict it's effect only to the file importing My.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        Well that's an issue if you want to set *My::Dumper = *Data::Dumper::Dumper once and for all. But if the only function defined in my is My::AUTOLOAD, and it always checks the calling package you should be fine wouldn't you? (At execution time at least, you'd lose compilation time errors with My::function() instead of function())

        > It's definitely possible to tell in which package a sub was declared.

        From Sub::Info

        my $cobj = B::svref_2object($sub); my $name = $cobj->GV->NAME; my $file = $cobj->FILE; my $package = $cobj->GV->STASH->NAME;

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Making it clearer to say that a sub is defined within current package (package aliasing)
by LanX (Archbishop) on Apr 11, 2019 at 17:20 UTC
    This is pretty clean, put your private stuff into a sub-package and alias that long package path it to a shorter package name at compile time.

    (of course these subs won't be able to call imported subs in the parent package without full qualification or inheritance ;-)

    use strict; use warnings; package X::Y::Z::my; # sub package sub xyz {warn "xyz called" } package Y::Y::Z; BEGIN { *::my:: = *X::Y::Z::my:: ; # alias sub package to my:: } # END { # delete alias at file's end # delete $::{"my::"}; # } my::xyz(); BEGIN { # delete alias here for demo delete $::{"my::"}; } package Other; my::xyz(); # fails alias already gone

    C:/Perl_524/bin\perl.exe d:/exp/local_package.pl xyz called at d:/exp/local_package.pl line 6. Undefined subroutine &my::xyz called at d:/exp/local_package.pl line 3 +0.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

    update

    the magic in the BEGIN/END blocks could be done in the importer of a module called with

    use  PackageAlias "X::Y:Z::my" => "my" and no PackageAlias "my"

    Update

    I just realised that Package::Alias already exists, even with a pretty similar interface.

    Skipped colons in my example to avoid confusion.

Re: Making it clearer to say that a sub is defined within current package
by RonW (Parson) on Apr 11, 2019 at 23:48 UTC

    Over in the Land of C, where there are no package or module specific namespaces, most of the project teams I have worked with use a 2-5 character prefix to designate which module "owns" each globally accessible symbol. These prefixes are abbreviations of the names of the modules they refer to.

    Also, most of my teammates use an IDE like, for example, Eclipse, that provide source code navigation aids. I use Eclipse with the EPIC plug-in for Perl. EPIC provides the "Perl smarts" so that Eclipse's navigation aids work with Perl.

    Anyway, with those navigation aids, like the "outline sidebar", "open declaration" and others, finding where a given function is defined is easy.

Re: Making it clearer to say that a sub is defined within current package
by hdb (Monsignor) on Apr 12, 2019 at 07:24 UTC

    Sometimes your editor has a function whereby you right-click on a name and say "Go to definition".

      "...right-click on a name..."

      This doesn't work for hardboiled coders because they think using a mouse is for sissies. Best regards, Karl

      «The Crux of the Biscuit is the Apostrophe»

      perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

        Such information can be triggered by cursor position too.

        At least in the mother of all IDEs ...

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: Making it clearer to say that a sub is defined within current package (Low Tech)
by LanX (Archbishop) on Apr 11, 2019 at 16:54 UTC
    The simplest low tech solution to your XY(Z) Problem is to define xyz in a dedicated namespace.

    DB<1> sub my::xyz {warn "xyz" } # declaration DB<2> my::xyz() # call xyz at (eval 10)[C:/Perl_524/lib/perl5db.pl:737] line 2.

    Be aware that this may collide with other modules using the same namespace.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1232441]
Approved by Eily
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (5)
As of 2019-04-20 10:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I am most likely to install a new module from CPAN if:
















    Results (109 votes). Check out past polls.

    Notices?
    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!