in reply to Questions about sharing code

There are a few modules on CPAN that I use over and over and over. Every single one of them exports a set of functions.

The one module that I should use more that I don't (and won't unless forced to) is File::Spec. I HATE the pseudo-OO interface with an absolute passion. Oh, I understand why it was written that way and I'm not sure I would've written it differently under the hood. But, the UI just sucks so badly that it hurts.

There are also a few modules on CPAN that I use a lot that provide an OO interface. This is because there is state information that needs to be preserved.

Yes - CGI is OO. The functional interface is an absolutely horrid and I refuse to use it.

The point is that you should provide a functional interface if they are side-effect-free functions. If there is data that needs to be preserved across function calls, then you should provide an object.


My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

Replies are listed 'Best First'.
Re^2: Questions about sharing code
by kyle (Abbot) on Nov 27, 2007 at 16:13 UTC

    The one module that I should use more that I don't (and won't unless forced to) is File::Spec.

    Have you looked at File::Spec::Functions?

      Yes, I have, but that's not a good example of an egregious interface. And, it begs the question "Why isn't that the default interface?"

      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re^2: Questions about sharing code
by bradcathey (Prior) on Nov 27, 2007 at 17:17 UTC

    Thanks dragonchild for the examples. It really helps to see what others are doing.

    So, what I'm hearing is: unless what I am doing is specifically OO or needing inheritance, I should just rely on the non-OO-Export method.

    Correct?


    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
      Use the right abstraction for the job. OO is a great pattern to work in, but it has a cost associated with it. Use it when necessary.

      Here's another way to look at it - C doesn't have any OO built into the language (leaving aside C++ for the moment). Yet, most stuff is written in C. Yeah, some of it could benefit from OO, but how well would math.h or stdio.h really benefit from it? Those are state-free functions. OO is for tagging state with behavior. If you have no state, then why OO?


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re^2: Questions about sharing code
by bradcathey (Prior) on Nov 28, 2007 at 15:29 UTC

    Not to flog a dead horse...

    What about:

    #tapping a locally placed file of subroutines use Mycommonroutines;
    OR
    #using the lib command to change the @INC use lib Mycommonroutines;
    OR
    #using Export to be more specific about what subs to import use Mycommonroutines qw(get_setup);

    Can I assume:
    1. use of lib in this case is ill-advised?
    2. the advantage (only?) of the Export route is to allow specificity of what routines are imported?


    —Brad
    "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
      First off, your second option doesn't compile.

      As for the difference between the first and the third, you need to seriously look at maintenance for the answer. For all the code in a given file, you can do a search for "sub foo" and find out what foo does. But, if it's a subroutine from another module, you can't easily find it. If there's just one Mycommonroutines, then you know where it is. But, what if there are 2? 5? 10? It gets a little hairier.

      It is for this reason that I and other CPAN authors have started using the following meme:

      # Explicitly state nothing will be imported use Scalar::Util (); # Call the function with the complete name Scalar::Util::weaken( $some_ref );

      For functions like weaken(), this may seem like overkill. But, this kind of habit will help maintainers because it becomes a flag to say "I didn't write this code in this file and this is where you can find it." And, conversely, if it doesn't have a prepended location, then it must be in this file somewhere. Any kind of hint like that will help your maintainer. Remember - code as if the guy who sees you code after you knows where you live and likes to swing axes.


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

        I still boggle at this idea. Much better would be:

        use Scalar::Util qw( weaken ); # ... weaken( $some_ref );

        For one, it abstracts the idea of weaken() so that you can easily replace it with a wrapper if you later need to wrap Scalar::Util::weaken() or use a weaken() from a different module (which improves the maintainability of your code). It makes the code easier to "parse by translating the shapes that form characters" instead of having to repeatedly be distracted by "parsing by translating the shapes that form characters" these long names, you can just "parse by translating the shapes that form characters" the perfectly descriptive "weaken" name; like using "read"/"reading" in place of the long quoted phrases above. It makes it easier to tell why Scalar::Util is being used. It confirms at compile time that the desired function is actually provided by the module and is not just meant for internal use or use as a method (something that isn't even clear to the person reading the code as you wrote it). It means that mistyping the subroutine name doesn't accidentally call an internal routine or method from that module. It allows the module to decide to not even load functionality that is not going to be used (or other, more advanced techniques such as maintaining backward compatability more conveniently).

        Module names need to be globally unique and so should be relatively long and descriptive, which is why I always try to make it so my module names only need to be mentioned once for them to be used. Repeating a magic string constant (the module name) over and over in your code does not make it easier to maintain. Do you avoid defining constants because it makes the code easier to maintain if the person reading the code can immediately see the value of the constant because the 7 is right there in front of their face? Or do you name your constants like SUNDAY_0, MONDAY_1, ... to get the best of both worlds?

        Repeating the source of the function at every invocation makes about as much sense to me as naming your local subroutines "sub frob_line237" so everyone will know which line to jump to in order to find the definition of the function.

        - tye