Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Re: Re^4: USE or Require?

by l2kashe (Deacon)
on Jul 24, 2003 at 19:11 UTC ( [id://277653]=note: print w/replies, xml ) Need Help??


in reply to Re^4: USE or Require?
in thread USE or Require?

exporting new isn't useful. Its there more to say to the user of the module, this is the way in. I'm in the camp that doesn't like namespace pollution, so I try to push my bias along to the next person. Either A) they will instantiate via the constructor, or B) they will have to fully qualify all calls via Package::Name::function

They will either use my module or not. Generally I encapsulate as much functionality as is reasonable in the module. I don't go overboard and include everything, unless its really needed (generally it isn't). The scope of the modules are also pretty closely tied to particular applications used on the network.

Im not quite sure why people aren't happy with my opinion on the topic. I find code written $obj->method(args) far cleaner, than trying to guess where the function or method is coming from. Since this is the case, part of my best practices is doing use Blah (); unless I simply can't (File::Find and Carp come to mind). My reasoning for only putting new in export_ok, is to simply let people know that the only thing that can be imported, if they want anything at all is the new method and since its in export_ok and not export they have to ask for it explicitly (which generally won't happen). I don't go in for the "shotgun to keep people out of the livingroom" mentality. I only document the public API. I tend to not do class inheritance. If I need functionality from multiple packages, then I encapsulate within my object in the constructor. (i.e $obj->{'socket'} = IO::Socket::INET->new()).

demerphq's thoughts are interesting, but my reasoning is much more shallow. If anything gets imported from a module of mine, then it won't overwrite anything in the end user's namespace. plain as that. It might not be the best mentality, nor the most technically sound reasoning, but my code looks consistant across the board (both in the scripts themselves and the modules I've written here), which makes it much easier for myself to maintain. On top of that the next person who comes along will be able to readily pick my style out, learn my idiosyncracies fairly quickly, and know what to expect in modules and scripts.

I wouldn't mind some feedback on what people's reasons are for not liking my view, as I'm trying to become slightly more refined in what I say. I have no "traditional" edjucation in computing or programming, but I've been at this since '99 and feel I have a solid background. Though hanging around the monastary has helped refine the way I frame my thoughts, I still feel like an outsider in terms of the appropriate lingo/vocabulary sometimes. I tend to be able to follow and use new concepts fairly easily. What I don't do well is communicate via text (voice Im slightly better at). So whats the beef?

use perl;

Replies are listed 'Best First'.
Re^6: USE or Require?
by adrianh (Chancellor) on Jul 24, 2003 at 21:49 UTC
    exporting new isn't useful. Its there more to say to the user of the module, this is the way in. I'm in the camp that doesn't like namespace pollution, so I try to push my bias along to the next person. Either A) they will instantiate via the constructor, or B) they will have to fully qualify all calls via Package::Name::function

    I wasn't having a go at ya - honest! I was attempting to understand you're motivations for having new() in EXPORT_OK, indeed for using Exporter in an OO module at all. Typically an OO constructor wouldn't work if imported into another package.

    I'm in complete agreement with your goal of making your code use intentions explicit in the code itself. However, I don't think that putting new() in EXPORT_OK is going to act as a useful cue in the way that you think.

    Normally an OO module will not use Exporter at all. If I see an EXPORT_OK my assumption will be that the code isn't OO - exactly the opposite of the point you're trying to get across.

Re: Re: Re^4: USE or Require?
by diotalevi (Canon) on Jul 24, 2003 at 21:23 UTC

    You just gave off a few whiffs of bad practice and I'd like to clarify just what you mean. There are some wrong answers here and I'm hoping you'll either confirm or deny that you're being sane.

    Exporting new() so it can be used as a function and not just as a method.

    use l2kashe 'new'; my $o = new; $o->foo( ... ); # Vs use l2kashe; my $o = l2kashe->new; $o->foo( ... );

    This is bad practice for a number of reasons. I'll cover whatever comes to mind just now. Keep in mind that I'm internally recoiling from the really hideous things you've brought to mind here.

    'new' is the common name for a constructor method. If the importing code already had a 'new' method or function then you've just overwritten the module's own new() method. 'use warnings "redefine"' will catch this and throw a warning but it won't produce a fatal error. In fact, because use() happens at compile time, which version of new() remains in your user's code is up to the order in which they were mentioned.

    use l2kashe; # The importing new() dies sub new { ... } # This one stays # Vs sub new { ... } # The original new() dies use l2kashe; # This one overwrites

    The expectation is that the constructor is tied to the class it originates in. Your constructor will have to hard code the originating class so that it can still reference l2kashe instead of wherever it was copied to. We all know hard coding data like this is poor form.

    sub new { bless {}, shift } # Vs sub new { bless {}, __PACKAGE__} # or maybe even: sub new { bless {}, 'l2kashe' }

    Your constructor has to function as a method and a function now. This is highly irregular and is one of the funkiest things that CGI.pm does. You have to include code in new() that decides whether $_[0] is the class name it should use (if it was called as a method) or whether it is just the first parameter (if it was called as a function). Properly done, the first argument is always the class name. That detail is handled by calling the constructor as a method - this is why you often see code that looks like this

    sub new { bless { @_ }, shift }

    In your case you have to distinguish between strings that are class names and strings that are parameters. Its possible but you've now eliminated the class name as an available value to the first parameter.

    sub new { my $class_or_parameter = shift; if ( $class ne __PACKAGE__ ) { unshift @_, $class_or_package; } bless { @_ }, __PACKAGE__; }

    You also intimated that maybe your other functions/methods also are expected to be called as both functions or methods. In this case you have to be even tricker and introduce significant evil to all of your code.

    sub something { my $self_or_parameter = shift; if ( ref $self_or_parameter eq __PACKAGE__ ) { unshift @_, $self_or_parameter; } ... }

    For the moment anyway, I can't see anything but really evil things down this path. Object oriented perl code just does not use Exporter. There is 100% no need for it. the expectation is that your constructor is a method call using the class on the left side of the arrow: SomeClass->new. Some people argue you should be able to do $o->new, I'll not address that but certainly out is SomeClass::new. Additionally, all of your methods that are not constructors are always called as methods and always with the left side being the object in question. So $o->method, never SomeClass->method or SomeClass::method.

      Ok, that makes sense, but that isn't what Im talking about. My constructors are always

      sub new { my $class = shift; my $obj = {}; # possible other initialization here # testing for args, making sure defaults are sane etc.. bless($obj, $class); # possible havy duty init stuff here # init new objects inside this one, make expensive # connections now, if its Ok to do that. $obj; } # END sub new

      In regards to the function/method conundrum.I only write methods. first line is always
      my $self = shift; or my($self,@other_stuff) = @_;
      I tend to have each method self sustaining and not necessarily reliant on other methods, I.e if inserting data, make sure its going to be clean when I pull it out, if some resource should exist (an open socket for instance) and it doesn't, either bomb out or possibly initalize it now. This way, if I have solved a problem once, then I can pick up a few methods from module_a, and drop into module_b with relative ease. I can see where you are going with the thread though, and I simply didn't realize the horrific road that could come from a simple @EXPORT_OK = qw(new); To me it means, the only thing you can get at is new, and I expected the coder to write $obj = l2kashe->new(@args); (yes I understand that expectations are not always what happens) I try to always provide at least rudimentary documentation, and always note the appropriate way to use the module is

      use L2kashe; my $l2 = L2kashe->new();

      Thanks for taking the time to clarify, and my apologies to the other monks for not understanding the depth of the issue. I'll just drop use Exporter and those variables from my code until truly needed. Again, thanks

      On a side note, it might do to put a note in the tutorial about stub module generation via h2xs -A -X -n some::module, as that is where I saw use Exporter, assumed it was good practice to use (yes I realize how bad that is now in hindsight), and defined what I thought was a sane value.

      A humbler monk

      use perl;

        On a side note, it might do to put a note in the tutorial about stub module generation via h2xs -A -X -n some::module, as that is where I saw use Exporter, assumed it was good practice to use (yes I realize how bad that is now in hindsight), and defined what I thought was a sane value.

        On a side note to your side note, if you're using perl 5.8.1 h2xs has a few more options that make a more "normal" OO template. My default is:

        h2xs --compat-version=5.6.1 --use-new-tests --skip-exporter -APX -n

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://277653]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-04-19 21:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found