http://qs321.pair.com?node_id=632779

clinton has asked for the wisdom of the Perl Monks concerning the following question:

I have 3 modules which all inherit from a base module, which contains (obviously) the common methods.

I also have a few 'static' subs which all the modules need to use, but they don't need to use them as methods, eg sub timestamp {} as they are utility functions and have nothing to do with the object.

The way I see it, I have two options:

The second option doesn't "feel" right, because timestamp is not a method, but it is certainly (a few lines) easier than exporting the sub.

What would you do?

thanks

Clint

Replies are listed 'Best First'.
Re: Common sub as method or function
by akho (Hermit) on Aug 15, 2007 at 16:16 UTC
    I'd separate them into Module::Base::Util or something like that, then use fully qualified names.

    That would make code easier to understand and maintain. Using $self->smth() is wrong because it creates an illusion you are using a method, exporting is usually bad just because it's bad, and Module::Base::smth() is bad because you have to remember which subs are methods and which subs are just there when changing Module::Base. You can put comments and say these subs should not be edited into methods. That convention will be broken despite all the commenting you can do.

    Utility functions should be separated into their own module.

Re: Common sub as method or function
by perrin (Chancellor) on Aug 15, 2007 at 17:30 UTC

    You should make them methods. If the rest of the API is OO, you may want to subclass things someday, and making these OO as well would be important then.

    However, these sound like class methods to me, not object methods. You should call them with the class name, not the object reference:

    my $class = ref $self; # if calling from an object method $class->timestamp();
Re: Common sub as method or function (or)
by tye (Sage) on Aug 15, 2007 at 17:32 UTC

    You can compile the code for methods in a package other than the one used to look up methods (the one matching the class name), then you can put non-method utilities in that non-class package and use them directly w/o inheritance and without poluting your "method name" namespace.

    package My::Class::SubClass; require My::Class::Base; @ISA= qw( My::Class::Base ); package My::Class::SubClass::_code; # Import common non-methods: My::Class::Base::_code->import( qw( _utilSub _timestamp ) ); # Export below methods to My::Class::SubClass: for( grep !/^_/, keys %{ __PACKAGE__ . "::" } ) { *{"My::Class::SubClass::".$_}= \&$_ if defined &$_; } use strict; sub new { my $us= shift @_; ... $us->someBaseMethod( @works ); _utilSub( _timestamp() ); ... }

    Then you don't have to try to guess whether the first argument is an object or not. Perhaps more importantly, you don't have to worry about your users being confused when they access a utility function that has nothing to do with the object via a simple $obj->_timestamp(), since that would tell them that there is no _timestamp() method.

    If you don't like the underscore, then you can list which methods to export rather than just exporting all subroutines that don't have a leading underscore.

    Just because Perl 5 doesn't include a feature for distinguishing methods from non-methods, doesn't mean you have to throw all of those different items into the same namespace.

    Note that this breaks SUPER:: and some naive OO doohickeys that try to rely on caller (which doesn't currently provide a proper class name for such uses).

    If you want your users to be able to access the util sub as a method, then you can use this same technique (of compiling methods into a package other than the one having the name of the class) to provide both a method and non-method version without having to try to guess whether the first argument is an object:

    package My::Class::Base; use vars qw( $VERSION ); $VERSION= 1.001_002; package My::Class::Base::_code; use vars qw( @EXPORT_OK ); BEGIN { require Exporter; *import= \&Exporter::import; @EXPORT_OK= qw( _utilSub _timestamp ); } use strict; sub _timestamp { # non-method code } # Method version: *My::Class::Base::_timestamp= sub { shift @_; return _timestamp(); }

    - tye        

Re: Common sub as method or function
by borisz (Canon) on Aug 15, 2007 at 17:27 UTC
    instead of $self->timestamp() you could write Module::Base->timestamp(). Maybe that feels better? Your choice :-)
    Boris
Re: Common sub as method or function
by shmem (Chancellor) on Aug 15, 2007 at 19:36 UTC
    You could use it as a lexical anonymous sub wherever you need it:
    my $t_sub = \&Module::Base::timestamp; my $timestamp = $t_sub->();

    No exporting, no pollution, and faster execution than with subs localized through the symbol table. The latter of course is the case only if you don't take a reference to the sub prior to each execution.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Common sub as method or function
by dogz007 (Scribe) on Aug 15, 2007 at 16:23 UTC
    You could also localize the sub you need into the main namespace:

    local *main::timestamp = *Module::Base::timestamp;

    It avoids exporting and still allows you to use just timestamp(). Be sure to document the localization though.

      It avoids exporting...

      It is still essentially exporting/importing, just without using Exporter.

      I completely agree with akho's recommendation to use Module::Base::Util, except that if we're only talking about two subs, it seems like overkill. (No doubt those two will become three, four, ten etc, so it is still probably the better way).

      My lazy tendency, however, would be, at this stage, to do as you have recommended.

      Clint

        I've reconsidered my answer, and I wonder if is there any functional difference between:

        local *main::timestamp = *Module::Base::timestamp;

        and:

        use Module::Base qw(timestamp);

        I think I would lean towards the latter being better practice. It is what I usually do, which makes me wonder why I came up with the local method first...