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

Hi, I'm looking to make a general Utility library, that might be used in three separate ways:

#(1) Via Object: (Does not use data from $self) $self->myUtility(@params); #(2) Via Class: Utility->myUtility(@params); #(3) Via Child Class: SomeOtherClass->myUtility(@params); #(4) Via direct call to function (Not OOPic) myUtlity(@params);

Why is this ? Simply because it is a requirement by the team. I am looking for a short efficient code stub to put in the entrance of the function to allow the function to run in 'OOP mode' or 'Normal' run-of-the-mill vanilla mode

Things I've used till now:

sub myUtlity { #1) Does not deal with case (2), expensive, unneeded stack change shift if(blessed($[0])); #2) Does not scale well to multiple params, does not handle a param li +ke the value "0" my ($self, $param) = @_; $param ||= $self; }

The basic requirements are stated above, with the addition of being small, quick, and generic for every possible function/param kind/number of parameters

---------- Update - Small fix due to typos, as some have noticed --------------

Replies are listed 'Best First'.
Re: Perl OOO Function Entrance
by choroba (Cardinal) on Aug 28, 2017 at 14:57 UTC
    Via Class should have been
    'Utility'->myUtility(@params);

    What you have is called a "fully specified name" and has nothing to do with objects.

    The first argument for cases 1 and 2 could be an object or a classname. Checking just for blessed isn't enough, as the first parameter could be an object of an unrelated class. You can rather try

    if (blessed $_[0] && $_[0]->isa(__PACKAGE__))

    and for a class name, you can do a similar thing:

    if (! ref $_[0] && $_[0]->isa(__PACKAGE__))

    but what if the first parameter can really be an object of the class? If the number of parameters isn't fixed, there's no general solution.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Perl OOO Function Entrance
by LanX (Saint) on Aug 28, 2017 at 14:59 UTC
    I strongly advice against using the same subs for different APIs.*

    If you really want to go with this strange requirement - what is $self supposed to be in non method calls (?) - you can use a different namespaces to mirror the function/methods.

    # UNTESTED package functional_Util; my $default_self= "something"; sub utility { unshift $default_self, @_; goto &methodlike_Util::utility; } package methodlike_Util; sub utility { my $self =shift; # ... do it }

    Of course you are free to use it the other way round if $self doesn't matter.

    Please note that Perl is flexible enough to autogenerate the needed "mirrors", either/or per

    • AUTOLOAD
    • inside an import routine
    • a BEGIN Block

    It's a trade: You are gaining large runtime performance and a clean interface for adding little compile-time complexity.

    But again I doubt the requirements are well thought off, sounds like a political compromise inside a big team meeting where everyone wants his own private menu in a restaurant just to prove he knows cooking.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    update

    *) since checking the arguments is complicated, error prone and slow, see other replies.

Re: Perl OOO Function Entrance
by stevieb (Canon) on Aug 28, 2017 at 14:59 UTC

    Here's an example. Note that you have to export the functions that you want to use without a fully qualified name, and your methods can't reference self (or, you have to check if $self is defined before using it, which means your code will operate differently in function or method mode).

    use warnings; use strict; package Obj; use Exporter qw(import); our @EXPORT_OK = qw(foo); sub new { return bless {}, shift; } sub foo { my $self; if ($_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__){ $self = shift; } my ($x, $y) = @_; return $x * $y; }

    The script:

    use warnings; use strict; use feature 'say'; use lib '.'; use Obj qw(foo); my $o = Obj->new; say $o->foo(2, 2); say Obj::foo(2, 2); say foo(2, 2);

    Output:

    4 4 4
Re: Perl OOO Function Entrance
by shmem (Chancellor) on Aug 28, 2017 at 17:00 UTC

    There are in fact only 2 cases, if $self is not being made use of in the subroutine:

    1. a method call
    2. a function call

    For a function call, no class resolving is done, so the function must be either fully qualified or its symbol imported into the current namespace.

    A method call is resolved via $class_name or $class_instance (object), and the sub gets the resolving thing as first argument.

    Conflating these two into the same thing just tells that the package is not meant to be object oriented. Losing the identifier of the call makes it impossible to dispatch to another class on which this one is based.

    use 5.10.0; package Animal; sub speak { my $self = shift; ref $self and $self = ref $self; say "$self says: ", $self->word; } package Cow; @Cow::ISA = qw(Animal); sub word { "moo" } package Dog; @Cow::ISA = qw(Animal); sub word { "wuff" } package main; Cow->speak; Dog->speak; __END__ Cow says: moo Dog says: wuff

    If you throw away the Animal, there's no way to make it speak.

    So, this is not about Perl OOO Function Entrance but adding syntactic sugar to plain function calls. For that, the following does the job:

    shift if $_[0] eq __PACKAGE__ || ref $_[0] eq __PACKAGE__;

    This can be added via simple filtering as the first line to the body of a subroutine.

    But I concur with LanX: I consider it bad practice to do such conflation on a regular basis, since it blurs the semantic differences of the various ways to call a subroutine. If that equality of dispatching is condoned within your team, it will bite you the first time when you use really object oriented modules from a third party.

    And you haven't considered other ways to call a subroutine. The following are all equivalent:

    use Animal; # contains Cow and Dog as above Cow->speak; speak Cow; my $animal = 'Cow'; $animal->speak; speak $animal; Animal::speak('Cow'); $animal = bless do{\my $x}, 'Cow'; Animal::speak($animal); $animal->speak;

    Of all computer languages I know, perl is the most language thing. And you can make most use of a language knowing and musing about its subtleties, and expressing yourself acccordingly without blurring them.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      I suppose you have a point. The base ideas was to create a class 'Object' which all classes inherit from. I did not like this idea one bit, seeing the methods utilized are Utility methods, and not 'Object' methods. Maybe it is a better idea to simple 'require' a regular script file with subroutines, or simply import all functions with something like this:
      sub import { no strict 'refs'; my $caller = caller; while (my ($name, $symbol) = each %{__PACKAGE__ . '::'}) { next if $name eq 'BEGIN'; # don't export BEGIN blocks next if $name eq 'import'; # don't export this sub next unless *{$symbol}{CODE}; # export subs only my $imported = $caller . '::' . $name; *{ $imported } = \*{ $symbol }; } }
Re: Perl OOO Function Entrance
by haukex (Archbishop) on Aug 28, 2017 at 14:54 UTC

    Your first example solution would probably be the way I might do this (although my step 0 would be to question this particular requirement ;-) ), although I'm not sure what you mean by "does not deal with case 2" unless your case 2 is a typo and was supposed to be Utility->myUtility(@params) instead of Utility::myUtility(@params)? In that case, you might have to go with something more like shift if @_ && (blessed($_[0]) && $_[0]->isa(__PACKAGE__) || $_[0] eq __PACKAGE__); shift if @_ && (!ref $_[0] || blessed $_[0]) && $_[0]->isa(__PACKAGE__); (Updated after choroba showed an even better way, calling UNIVERSAL's isa as a class method)

    I'm also not sure if that's really too "expensive". Sure, you could do trickery like my ($foo, $bar) = cond ? @_[1..$#_] : @_; or even my $I = cond ? 1 : 0; my $foo = $_[$I+0]; my $bar = $_[$I+1];, but that's getting pretty ugly and unwieldy. Or, if you wanted to get really creative, have &Utility::myUtility always discard its first argument, and but when you export it to someone else's namespace so they can call it as in your third example, actually export a wrapper sub that inserts an extra argument before calling &Utility::myUtility (Update: kind of like LanX has shown in the meantime). But whether all this is really necessary depends on what your benchmarks and profiling show about whether the above shift method is "too expensive".

Re: Perl OOP Function Entrance
by AnomalousMonk (Archbishop) on Aug 28, 2017 at 17:08 UTC
    Why is this ? Simply because it is a requirement by the team.

    Judicious application of rat poison to the morning coffee may result in team requirements that are much more reasonable.


    Give a man a fish:  <%-{-{-{-<

      It's not nonsensical if the intention was to have a "with" mechanism like in JS. (I doubt that's what the OP wanted and upvoted your post)

      I.e. calling methods like functions inside a block which defines the target object and avoids redundancy.

      See also http://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/with

      NB many listed contras are JS / implementation specific.

      AFAIK it's kind of a (pseudo) DSL technique in Ruby to call methods with syntactic sugar (though I'm ignorant about the implementation there)

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

        It's not nonsensical if the intention was ...

        I suspect that some or all of the rationales you suggest may be in play. Impossible to know, of course, unless Mano_Man is more forthcoming. However, the justification given in the OP ("Simply because it is a requirement by the team.") suggests that "logic" of the "We're doing it this way because this is the way we're doing it" school may be at work, which makes the rat-poison line of argument all the more attractive.


        Give a man a fish:  <%-{-{-{-<

Re: Perl OOP Function Entrance
by zakame (Pilgrim) on Aug 29, 2017 at 03:05 UTC

    At one point during my development of Hashids for Perl I thought about something like this, especially for some utility methods that I ported from the original JavaScript source. On one hand, there's a "seductive" thought of making all things OO-ish for a "consistent" interface, but ultimately I found that the utility methods were, after all, utility, and could be plausibly used out of the Hashids object context.

    So in the end, I decided to make a Hashids::Util module that implements subs only, not methods; just add a bit of Exporter magic to provide the subs on by-name basis (or use Hashids::Util ':all' if you want them wholesale) and namespace::clean on both the module and requestors so that those subs don't pollute the namespace.

    Here's a basic template for such a Util module that you might want to use:

    package Util; use strict; use warnings; our ( @EXPORT_OK, %EXPORT_TAGS ); BEGIN { use Exporter 'import'; @EXPORT_OK = qw(foo bar baz quux); %EXPORT_TAGS = ( all => \@EXPORT_OK ); } # Dependencies for this Util module go here... use namespace::clean -except => [qw(import)]; sub foo { my ( $x, $y, @z ) = @_; ...; } sub bar { 42 } sub baz { ... } sub quux { ... } "Derp."; __END__
    I suspect that another possible way for you would be to go implement those utility methods in a Role, and compose this Role in to your other classes; that might be a better fit for your situation, as you mention in another reply ("The base ideas was to create a class 'Object' which all classes inherit from.") We could turn the example above into something like this (assuming using Moo for OO, change needed if its something else:)
    package Class::Util; use Moo::Role; sub foo { my ( $self, $x, $y, @z ) = @_; ...; } has bar => ( is => 'ro', default => sub { 42 } ); ...; 1;

    Then in your classes you just add with 'Class::Util';.

    EDIT: I realized you can actually combine the two approaches above rather easily (though at a cost of a bit of conceptual overhead.) Here's one rather naive approach:
    package Class::Util; use Moo::Role; use Util (); for my $method (@Util::EXPORT_OK) { no strict 'refs'; *$method = sub { my $self = shift; my $sub = "Util::$method"; $sub->(@_); }; } "Derp.";

    Now you can have a regular Util module for standalone subs, and a Class::Util role for objects.

Re: Perl OOO Function Entrance
by LanX (Saint) on Aug 28, 2017 at 14:46 UTC
    OOO = Object Oriented Omnipotence? ;-)

    SCNR...

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Actually, the culprit of my typo was Out Of Order execution, a common term here, but hey - good pun.
Re: Perl OOP Function Entrance
by Anonymous Monk on Aug 29, 2017 at 12:29 UTC
    Do it one way ... pick any one. Computer software is never a place where you want to have more than one way to do one thing, Tim Toady notwithstanding. The manager or leader of the team should curry comments and recommendations from the team, with all due respect to each of them, and then choose one and require that it be used throughout the system. Authority has its privileges ...