tags:

views:

159

answers:

7

I'm writing a module that has several methods. Let's consider this :

package MyPackage;
sub new {
 ...
}

sub do_your_job {
 ...
}
1;

What is to stop someone calling do_your_job like MyPackage->do_your_job instead of $obj->do_your_job ? Do I need to check in every method that I'm receiving a reference as the first argument?

A: 

Use Moose and forget about the plumbing.

at least explain how this is relevant to the question
ysth
Easy. You don't worry about that kind of things when using Moose. And yes, the first line of a Moose method is "my $self = shift;".
"You don't worry about that kind of things when using Moose" isn't an answer. Does Moose check the type of the object or doesn't it?
Kinopiko
at least show how his sample method declaration would be coded using Moose
lexu
It would look exactly the same, because Moose doesn't change anything at all that's relevant to the question -- you declare methods the same way, and they can still take either the class or an instance as invocant. MooseX::Declare would *allow* you to put a check into the method signature, but it still does nothing about it automatically.
hobbs
There's a meta-question here, namely "Is it a good idea to use Moose and not know anything about the guts of Perl's OO system?" My instinct is "No", but on the other hand, I don't know anything about the current C guts of Perl. (Not competent in C to do much about that yet, but it's on the list of things to do.) Obviously at some level, pretty much every sane person has to stop somewhere. But I think stopping at Moose is too early.
Telemachus
Moose doesn't code your methods for you, unless you're talking about an attribute accessor. And the problem is just the same in Moose as otherwise -- if you try to do something with the particular object instantiation that the method was (presumably) passed and it's only a classname, you'll get an exception.
Ether
+8  A: 

No. If your module features only the most minimal documentation, it should be clear from that that do_your_job is a method that needs to be called on an object. If someone wants to call that in some other way, it's his fault.

Of course, you could check whether the first parameter to your method is a blessed object. But this has at least two drawbacks: You lose a little bit of performance and you clutter your methods with code that really doesn't do anything promised by the method's name.

innaM
I understand what you're saying. But, I should check on all the instance methods, and die with a detailed exception anyway, right? For those users that can't be bothered to read the documentation.
Geo
I edited my answer.
innaM
@Geo: no not necessarily right. At some point you have to trust the user; if you have two methods bar and baz, you aren't magically going to be able to tell that a call to bar was meant to be that, not a call to baz where they forgot the exact method name.
ysth
@Manni: I don't think you meant *mess up* ( http://www.google.com/search?q=define%3Amess+up ) but rather something like *"your methods become messy with "*.
Sinan Ünür
It sounds better after your edit. But isn't a messed up method one that became messy?
innaM
@Sinan: The 'get' was a typo (I'm guessing), but how is "your methods become messy with" any different from "you mess up your methods with"? The link you gave offers "make a mess of" as a definition of 'mess up', so I'm not seeing the any distinction here.
Telemachus
mess(y) removed and replaced with "clutter". _I_ am happy now.
innaM
@Manni, no "messed up" has a connotation of "broken" that "messy" doesn't.
ysth
Ah, ok. Thank you. I guess "cluttered" was what I was really aiming for.
innaM
+5  A: 

No, you don't need to check; you can just document that it's an object method and trust callers to use it as such. But if you feel that's insufficient, you certainly can check.

Same thing applies to arguments other than the method-call implicit object/class argument. You might have a parameter that's supposed to be an array ref, or a positive integer less than 10, or an object of some given type, or just about anything else. One path is to assume callers follow the documentation; the other is to trust nothing.

ysth
+5  A: 

The short answers in theory are "Nothing" and "That's not a bad idea."

In practice however, the documentation is what solves these problems. There's a common courtesy in the Perl community that instructs developers to only use modules in the manner which they have been documented.

Perl Modules come with no guarantee that the internals will never change. So an implicit agreement is that developers will not dig into the internals of a module/class unless they are implementing that module/class.

EmFi
A: 

If I use Perl's object-oriented facilities, inside each module I always create a routine to check the argument with something like

sub _check_object
{
    my ($log_db) = @_;
    die "Bad object" unless ref $log_db eq __PACKAGE__;
}

and call it at the head of each subroutine which takes the object as first argument:

sub some_routine
{
    my ($log_db, @other_stuff) = @_;
    _check_object ($log_db);
    # continue ...
 }

I'm highly error prone, so I don't trust the user, even if the user is me. I've tripped up several times by not doing this, which is enough reason for me to always do it. Perhaps I am clumsier than other people, I'm not sure. Anyway if this turned out to affect performance I could remove it again, although nothing I do in Perl really needs to go that fast anyway.

I have this weird idea that the reason people keep recommending Moose is because it does things like this automatically; however, I don't know if that is the case.

Kinopiko
This will break as soon as you use inheritance.
innaM
I don't use inheritance.
Kinopiko
*You* may not use inheritance now, but by taking this approach you're stopping anyone else (including possibly even yourself later on) from doing so with your code. You're also getting yourself into a habit which will be counterproductive if you ever write code for anyone else.
Tim
I hear the knocking of the language police on my door.
Kinopiko
@Kinopiko you can write all the bad code you want, but you should probably spend less time telling other people that they should write bad code too.
hobbs
@hobbs: This isn't bad code. It works fine.
Kinopiko
It's not just bad, it's fantastically stupid. It's extra code and extra complexity to do something that is not only useless but *actively harmful* because it makes your classes impossible to extend. If you wanted to check that `ref` or `blessed` was true, I would say okay. If you were checking `isa` I would say you're wasting your time but that's your business. But checking `ref` for equality is unquestionably bad code.
hobbs
@hobbs: I don't want to extend my classes. I really don't. This code is fine for me and works to catch errors, so I'm quite pleased with it. You don't have to use it if you don't like it.
Kinopiko
I don't think that hobbs or anyone else felt compelled to use your code. But putting code like that in an answer on SO is going to increase your chances of getting downvoted.
innaM
What error, exactly, does it catch, that a test for `ref` being true doesn't? The one where you write `$some_object->Some::Other::Package::method($args)`? One assumes that if you went to all the trouble of writing *that*, you probably meant it!
hobbs
@hobbs: Are you serious?
Kinopiko
A better check, that still works with inheritance, would be `die "Bad object" unless $log_db->isa(__PACKAGE__);`
Ether
Yes, and you could inherit from a module that implements _check_objects without putting that method in each and everyone of your modules.
innaM
@Kinopiko YOU might not want to extend your class, but someone else will down the road and they will curse your name for your hard-coded inflexibility. That "someone else" might be you in six months time when the world will be a different place. Things change. Furthermore, your stubborn defense is particularly odd given that the proper code is a trivial change to one line. I smell fear.
Schwern
hobbs: playing devil's advocate, it would catch: use bigint; method_accidentally_called_as_sub(3)
ysth
This answer gets an upvote from me. If you feel the need to validate and have no need for inheritance (and inheritance in Perl is so often horribly misused that this isn't as appalling as some are taking it) this is an effective way to do it.
ysth
It's strange that so many people suddenly appear and downvote on these Perl questions. Even stranger, it looks a lot like some of these people didn't even read the question at the top of the page, so I'm not sure how they found their way down to the bottom of the page. Quite similar to this question, http://stackoverflow.com/questions/1578185/, where I got several successive downvotes even though what I said there is entirely correct. One mistaken downvote could be explained, but this sudden rash of downvotes looks very suspicious to me.
Kinopiko
What are you suggesting? That there's a mob of angry Perl folks that downvotes whatever you say? Come on! What we are seeing here is a result of the inability to downvote comments. What was really downvoted was your "I don't use inheritance." comment. You didn't ask the question and it's not for you to decide whether the OP should use inheritance or not. Changing your answer to something that works with inheritance would have been the way to go.
innaM
@Manni: I suspect that people are being directed to downvote posts through an off-site channel of communication. In other words I suspect someone is telling people to just go and downvote an answer. It's bleedin' obvious that one of the guys commenting above on my answer hasn't even read the original question. How did he find his way here without reading the top of the page? That doesn't make sense. The link I included to the other question is very similar. It's not just this particular question but several other instances of the same thing happening which make me suspicious.
Kinopiko
So what post are you talking about? I tried to guess, but failed.
innaM
+3  A: 

As others have said, document what your module does and trust the user. However, it is worth thinking about the philosophical difference between a call on a class and a call on an object. If your Perl module provides a way of creating objects, many of the methods in that class are likely to rely on data specific to an individual object. This is probably the most common way of calling methods: once you have an object, you simply do $object->method(…). However, it is likely that some class methods are general and do not need data from a specific object. These are often called using the package name, and this is most commonly seen in calling a new() method, such as LWP::UserAgent->new().

Conveniently, Perl allows you to treat these two cases in the same way, using something like my $self = shift; at the beginning of the method: if the method is called on an object, $self gets the value of the object reference, if called on the class, $self gets the package name.

Even if the method is called on a class, so $self contains the package name, you can still call other methods on this package in the same way by doing $self->method(…). This is then compatible with calling the original method on a specific object, so everything holds together.

As just one example, the Perl Math::BigInt package includes quite a lot of methods, some of which are designed to be called on the package (class) itself (e.g. new()), some of which are to be called on individual objects (e.g. round()), and some on either (e.g. accuracy()).

The moral: it should be up to the functionality of the method as to whether it is capable of sensible meaning. It's reasonable to assume that the any method is being called with the expectation that it will do what you advertise it to. However, don't require a method call to be on an object when it might make sense for that method to be called on the class itself.

Tim
+3  A: 

You don't have to check the referent of a method call. However, based on what your specific application needs, you may want to:

 sub some_method {
      my( $class ) = @_;
      croak( "This is a class method only" ) if ref $class;
      ...
      }

Although I tend to not care so much, you might want to check that you have an instance before you try dereferencing something:

 sub some_instance_method {
      my( $self ) = @_;
      croak( "This is an instance method only" ) unless ref $self;

      $self->{some_attr};
      }

I tend to assume that the higher level will call it as I've documented it, though.

brian d foy