Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

"I Only Wanted a Hammer, Not the Whole Toolbox."

by luis.roca (Deacon)
on Nov 21, 2011 at 01:29 UTC ( #939123=perlmeditation: print w/ replies, xml ) Need Help??

I'm going to eat a bit of my own dog food here. Sometimes I'll see a good conversation going on in the Chatter Box that, after a while, I think "This should be a post so more people can benefit and participate." I'm often amazed at how a Perl question that can seem fairly straightforward surprises with varied answers, discussion and some healthy debate. Several people (I'll let them identify themselves and chime in if they wish) taught me a lot more than I expected in the CB today. What follows are thoughts that came out of that conversation as well as a few things that have been on my mind.

"Is it good practice to import only the functions of a module you intend to use?"
I naively asked in the CB if, when and why it would be good practice to specify module functions used in a program. The question came from my use of Math::Matrix last week in a response to an SoPW about multiplying matrices. Somewhat inspired by a book I've been reading on Unix philosophy and my own experience in how I prefer to use graphics programs, I started playing with the code I posted.

For years when I use Adobe Illustrator I hide all the pallets and work in fullscreen mode from the start accessing exactly what I need through key bindings. Only when there is something that has no bindings will I call a pallet or *sigh* the main menu :) . I'll also go as far as deleting entire libraries, colors, brushes, symbols etc. that I know I will not use for the project erring on the side of going overboard since I can always bring something back in.

I'm taking this attitude further by beginning to build my own color, shape and Beziér curve libraries using Cairo as well as a few other Math modules for projects. This way when I open up a new file in Illustrator I have available no more than what I need. A more frugal approach as opposed to starting from a blank document, creating an image, adding and taking away from it only to realize toward the end of the day what a mess I've made. *I go back the next morning, open a new document or copy the first and begin the painful clean up.

I'm discovering I would like to work in a similar way when I program. If I decide to use Module::Name, I would prefer to be able to specify what part(s) of the module I will use adding features as needed.

*Going back to Math::Matrix a few things happened when I naively (more like ignorantly) went back and added 'multiply'.

  1. Math::Matrix is an OO module.
  2. I called a function that didn't actually exist since, again, OO module. Whoops :)
  3. * The script worked fine with the addition. No warnings, failures etc. If I didn't ask about this in the CB and referenced this specific script I would have left it as is.

Unfortunately it seems not every module is designed to facilitate the style of working I'd prefer. There are OO modules, there are modules that export all their functions, modules that export some of their functions and finally modules that call on it's user to import the functions as needed.

Does that mean I won't use modules I'd otherwise find helpful? No, but it would be nice if I had the option to pick and choose what I wanted. I guess if and when it bothers me enough, I'll sit down and write my own modules. Maybe that's not such a bad side effect.

To the monks who gave up their time on a Sunday afternoon — Thank you.


  • There are ways I've avoided this over the years (eg. Dividing the illustration or design into separate files, creating templates and custom pallets) but with large programs like Illustrator there is a ceiling you hit when it comes to being efficient.
  • There's also a bit of business on why use Math::Matrix 'multiply'; didn't fail. It was discussed in the CB and, as I understood it, had something to do with Exporter.pm. I'll let smarter monks explain what that 'something' is if they wish.


"...the adversities born of well-placed thoughts should be considered mercies rather than misfortunes." — Don Quixote

Comment on "I Only Wanted a Hammer, Not the Whole Toolbox."
Select or Download Code
Re: "I Only Wanted a Hammer, Not the Whole Toolbox."
by davido (Archbishop) on Nov 21, 2011 at 07:50 UTC

    Let's look at what's actually going on when a module exports a subroutine. Say, for example, that in module Foo there is a sub named 'bar'. You use Foo, and Foo exports 'bar' to the caller's namespace. If package main used 'Foo', then Foo will export bar() to main. The way it does this is by assigning \&bar to *main::bar. Symbol table manipulation.

    Now main:: has a subroutine named bar(), which happens to be the same piece of code as the one originally called Foo::bar(). Both names are an alias to the same function.

    The problem when a module does this without being asked is that of namespace pollution, and namespace conflicts. LWP::Simple exports get(). Maybe some other module also exports get(). You can't have two gets in package main. Conflict. Modules that export without being asked to are the worst perpetrators because they may export several names into your namespace and it's up to you to read the docs and figure out what they're doing to you. If every module did that we would have all sorts of interoperability problems due to name collisions.

    Fortunately most modules that export allow you to specify exactly what you want to import into the calling namespace. List::Util is such a module. You have to tell it explicitly that you want max(). That's good, because then if you also want to use some other module that has a max() you can decide which module's max you will import, and which you will invoke by fully qualified name.

    I would say that whether or not a module automatically exports, it's never a bad thing to enumerate on the 'use' line what it is that you wish to import from the module. It may seem pointless to specify use File::Find qw/find/; when we know that File::Find already automatically exports find(). But someday you may look at your code again; the code that uses six modules... and you'll have a hard time remembering where find() is coming from. Enumerate it on the use line, and it's easy to look at the code and recall, "Oh, find() is from File::Find, not from My::Lost::Keys::find()."

    Another reason to specify what you want to import is that many of the modules that export do use @EXPORT_OK rather than @EXPORT, which means that you the user get to decide what you want to bring into the calling namespace.

    There's another nice trick too. If you call use My::Module ();, by specifying an empty import list the module's import() function will not be invoked, and the module will export nothing to the caller's namespace. use File::Find (); find(); will complain that &main::Find is undefined. See? We just prevented File::Find from polluting our namespace. We can still invoke find() by using its fully qualified name: File::Find::find().

    Now for OO modules. Those modules don't typically export anything. There are exceptions, such as CGI.pm which has both a function and a OO interface, but most OO modules keep things pretty clean (we hope). That being the case, there's no need or benefit to specifying an import list on the use line. In fact, even the reason I gave earlier of being able to look at code and know where the sub is coming from no longer applies, since OO modules use referential notation to access the methods. Module->method(), $class->method(), $object->method(). All we have to do is look to the left of the -> to know who owns the method (inheritance nightmares notwithstanding).

    And again, as OO modules use referential notation, and don't export anything, there's no namespace pollution. There's no good reason to say, "Well, I need Math::Matrix, but the only sub I need from it is multiply()." Doesn't matter. The other functions are built into its api, but they just sit there in case you need them. They're not being brought into your caller's namespace. They're not hurting anything really.

    I chimed in here to try to clarify some of the points I was making in the CB. I know another person or two were working along other points as well; one issue was why Exporter doesn't throw a fatal when someone specifies an import list on an OO module that doesn't export anything (it ought to, but doesn't being the assertion).

    I hope some of this is helpful, and even more, I hope that most of it is accurate and coherent. ;)


    Dave

      Modules that export without being asked to are the worst perpetrators because they may export several names into your namespace and it's up to you to read the docs and figure out what they're doing to you. If every module did that we would have all sorts of interoperability problems due to name collisions.
      No module that uses Exporter actually does that. However, Exporter has a feature that says, "you, exporting module, the importer requests to import your default set of exports, do you have any?". Many people think this is "the exporting module exports without being asked", but that isn't true.

      There are grasshoppers who think one should never have a non-empty @EXPORT. Those people are either clueless or just repeating others without actually using one of their own braincells. They don't seem to realize that if you prefer importers to list what they like to import:

      use Module1 (qw[sub1 sub2 sub3]); # Import three subs. use Module2 (); # Import nothing.
      is doesn't matter at all what the exporter has in @EXPORT.

      It's only when the importer writes:

      use Module; # Import defaults.
      that is, when the importer asks for defaults, you get what's in @EXPORT. And providing reasonable defaults, so reduce programmer load, that's good practice. Think about it. People complain they have to type use strict; over and over again. Would you have preferred strict not to have defaults, so you'd have to type use strict 'refs', 'vars', 'subs'; instead? Or would you want to list all the warnings classes you want to have enabled?
      Another reason to specify what you want to import is that many of the modules that export do use @EXPORT_OK rather than @EXPORT, which means that you the user get to decide what you want to bring into the calling namespace.
      See, that's the heart of the problem. People do not seem to realize that having an @EXPORT does not prevent you from listing what you want to import.

      Good practice is to actually think, and decide what good defaults are for your module. Sometimes, the answer is "no defaults". Far more often it makes sense to provide a non-empty set of defaults. Having defaults robs the user of nothing; having defaults optimizes for the common case.

      And again, as OO modules use referential notation, and don't export anything, there's no namespace pollution.
      OO modules are actually worse. You cannot control what your parent class "exports". Sure, it's not taking a slot in the stash of your package, but with OO, dispatch isn't just done in your package (as with non-OO code), but it's searched through the entire @ISA tree. If you do our  @ISA = qw[Module], your objects will have any subroutine defined in Module as a method; and no control is provided which methods you want or not. You'd need a set of tiny partial classes, which you then mix-and-match using MI (or roles, but roles are just MI without using the word MI). Of course, then you're back to the same problems you mentioned before, from which class did we inherit method, or what if we want to use two roles that both use methods with the same name? Note also that if you import a subroutine with the same name as a subroutine you already have, or if you import subroutines with the same name from different namespaces, a warning is generated (assuming you have warnings turned on). That doesn't happen with OO. Perl won't tell you you're inheriting two identical named methods. Or that one of your methods is masking one in your parent class.
        roles are just MI without using the word MI

        ... except for compile-time resolution and composition and allomorphism.

        Perl won't tell you you're inheriting two identical named methods.

        A real implementation of roles solves this problem.

        what if we want to use two roles that both use methods with the same name?

        You get a compile-time error and must disambiguate the conflict yourself.


        Improve your skills with Modern Perl: the free book.

Reaped: Re: "I Only Wanted a Hammer, Not the Whole Toolbox."
by NodeReaper (Curate) on Nov 21, 2011 at 13:04 UTC
Re: "I Only Wanted a Hammer, Not the Whole Toolbox."
by Anonymous Monk on Nov 21, 2011 at 13:34 UTC
    Namespace collisions are sufficiently a problem that to list only the names you actually want to use is often a good thing.   I guess it depends a lot on how many packages you use, and on whether there would be ambiguity in the readers' mind about exactly what routine is intended. Above all else, make your intentions clear.
Reaped: Re: "I Only Wanted a Hammer, Not the Whole Toolbox."
by NodeReaper (Curate) on Nov 25, 2011 at 12:58 UTC

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (6)
As of 2014-09-30 20:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (383 votes), past polls