Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Re: Re: Re: Context aware functions - best practices?

by ihb (Deacon)
on Jan 20, 2003 at 12:41 UTC ( #228333=note: print w/ replies, xml ) Need Help??


in reply to Re: Re: Context aware functions - best practices?
in thread Context aware functions - best practices?

First of all I want to point out that my reply to which demerphq replied should be read in its right context: as a reply to a question about wantarray and proper usage.

Funny. There a number of situations where ive taken advantage of this very feature [passing extra arguments].

There's a big difference between seeing where you can take advantage and have a desire to take that advantage. I can see when you might want to do it, but I've never felt I actually want to do it. When you do it it's quite likely that you've written your dispatch routines yourself. And if that's the case then I see no problem with it. You've hopefully documented that extra arguments will be ignored and that it's safe to do that.

However, when I use such techniques myself I've found that I do it mainly when the routines takes the same arguments. Sometimes the routines are generalized to ignore some arguments though, just to fit into the template. But they're almost always related. But even if they're not, I'm not all convinced it in the general case it A-OK to pass extra arguments. I'm not into too much stricture, so I wouldn't disallow it. But I hope that the programmer doing it is aware of that it can lead to bugs in the future, like when a module is updated. Module authors expect their users to follow the manual. I wouldn't hesitate a second to add an extra argument to a subroutine if I had documented the subroutine to take a specific number of arguments. But whilst I'm against excessive stricture I still want to help the programmer discover his error. If I see a great potential for a user to error I consider putting in a warning. In my argumentation to which you replied, that is the case. There is a great chance that if the warning is issued something unforeseen or unintended happened in the user's code.

When calling the overriden sub its fairly natural say something like sub blah { $foo->SUPER::blah(@_); ... }

This does not relate much to my post, as far as I can see. But this is not an obvious issue, as I understand it, and could be the topic of another discussion.

So IMO warning when someone passes too many parameters seems a little extreme.

I can't see where I've advocated this. I've recommended programmers to use the same philosophy as Perl itself does when it comes to warnings. For instance, @foo = $bar, $baz gives a warning in void context, because most likely the programmer didn't intend it to be (@foo = $baz), $baz. I'm saying the same here: Warn if it looks like the programmer did something he didn't want to, but also give a way to disambiguate and get rid of the warning. In my case above, that simply means you make a list assignment instead of a scalar assignment.

# Consider a subroutine with a 1-to-1 # argument to return length mapping. my ($a) = foo($bar); # OK my $a = foo($bar); # OK my ($a) = foo($bar, $baz); # OK my $a = foo($bar, $baz); # Warns. # Why did the programmer pass two arguments # if he just wanted one return value?

In real life it could look like this:

  my $a = foo(burk());

and &burk is believed to return just one element. Perhaps the user was wrong in that, and &burk returned more than one element. Or perhaps &burk needed to be called in scalar context to give the expected return behaviour of one element, like localtime(). If I'm reading you right, you would be annoyed by that it warns?

I also would like to extra clearly point out that I wasn't talking about arguments length checks per se. I was talking about return list length. Checking against @_ is just a way to in a simple way often be able to check the return length. Again, my statements in my reply must be read in its proper context. I was talking about my &get_headers method, that uses one-to-one argument to return length mapping. If you read my rule of thumb (which is just that; a rule of thumb and thus has exceptions) again you'll see that it's generalized to be about the output rather than the input. That ought to be clear in the code illustration.

If you do that then I at least hope that you use warnings::register;

Or I use a global like $Pkg::WARN so pre-perl5.6 programmers can control the warnings. Or a combination of the two. The functionality warnings::register gives you (described in perllexwarn for those that are unfamiliar with it), is unfortunately not so well known, nor used. I think it should be given more attention.

... so that I can turn off the annoying messages instead of having to write tortured code just so your sub gets the correct number of parameters.

Or you could just disambiguate... don't torture yourself when you don't have to.

Me: I'm especially paranoid against other programmers
You: Im sympathetic to this postion, but I think its a fine line between being paranoid, and being unduely restrictive.

I'm not saying that I'm paranoid against my users. I'm not afraid of their abuse of my code. I'm paranoid when I am the user. I choose to write my code to be restricted by the documentation of the module I'm using. If the documentation says the subroutine wants three arguments, then it gets three arguments. If I break this deal between me and the module author I have no one to blame but myself. So I do as the documentation tells me.

If the end user wants to give you more arguments then you need, then theres a decent chance that decision was a sound one.

Not in this case! It's most probably a slip-up, and I want to be fair and warn the user. See the example above.

I certainly disagree with code that makes it impossible to do something just because the author couldnt see any reason why anyone would want to do it. (A position that you arent necessarily advocating I realize, but you can see the relevance I hope.)

I sure don't advocate that. I don't see the relevance, but I trust you that there is. But while on the topic I can just as well elaborate a bit. Why I definately not am advocating this is because I believe some of my users to be smart--often a lot smarter than I. So they might come up with cool tricks that follow by the unrestrictive interface. And I want to let people be creative. Sometimes though, restriction can be accepted. One of those cases are when you see potential future constructions that might necessarily restrain certain things. It's better to start restrictive in such cases, and easy up the rules later on. If users are significantly disturbed by my restrictions they'll hopefully let me know. If they come with good arguments, I'll sure consider it. But I don't want to paint myself in a corner due to premature design. (CPAN has examples of this.) Some modules need a couple of releases to grow, and initially you want to keep your doors open. I much prefer this over having the whole module marked as experimental, since that probably leads to that the people who actually want/need to use it in real-life situations won't, and I won't get the feedback I need on it.

ihb


Comment on Re: Re: Re: Context aware functions - best practices?
Select or Download Code
Re: Re: Re: Re: Context aware functions - best practices?
by demerphq (Chancellor) on Jan 20, 2003 at 19:10 UTC
    Good reply. ++. Like your previous node theres a considerable room for digression onto other topics. Something that I certainly did in my reply to you.

    :-)

    Hmm. I have to admit that my reply didnt take full account of the context that you were discussing. Let me put it this way, I probably wouldn't write

    sub my_lc_warn { not defined wantarray and warnings::warnif("void", "my_lc used in vo +id context"); unless (wantarray) { @_>1 and warnings::warnif("my_lc called in scalar context with ex +tra arguments (".join(",",@_).")"); return lc shift; } else { return map lc $_,@_; } }
    preferring
    sub my_lc { not defined wantarray and warnings::warnif("void", "my_lc used in vo +id context"); return wantarray ? map lc $_,@_ : lc shift; }
    but I can see your point that
    my $lc=lc("A","B");
    would most likely be an error, and thus the warning could be useful.

    I admit that my comments more directly dealt with the general case that I know realize you weren't making. Although I still think that to a certain degree you were arguing the more general case than narrowly confining yourself to the specific one.

    A few comments to some particular things you mentioned, that may or may not be a digression. :-)

    You've hopefully documented that extra arguments will be ignored and that it's safe to do that.

    On the contrary. Perl idiom is relatively clear about what happens to supefluous arguments. They get ignored. So if a routine that does anything other than quietly ignore arguments should document it as so, not the other way around.

    can lead to bugs in the future, like when a module is updated.

    Well, I agree that when a module is updated and new arguments are added to old subroutines there is a possibility of problems. But this is an API change and as such means that careful checking will have to be done when upgrading to ensure that no problems arise anyway, so I'm not sure how good an argument this is.

    There is a great chance that if the warning is issued something unforeseen or unintended happened in the user's code.

    Yes. I agree with this point in this context.

    This does not relate much to my post, as far as I can see.

    But it does. Ive seen enough code where overriden methods get new arguments appended in child classes to see this as relevent. (Again i'm refering to the more general case.)

    I choose to write my code to be restricted by the documentation of the module I'm using. If the documentation says the subroutine wants three arguments, then it gets three arguments.

    Well on this one im not so convinced. Does the documentation say Takes three arguments... or does it say takes three and only three arguments...?. Unless the documentation is extremely specific the general conventions of the language need to be taken into account. One of them is extreme flexibility in handling arguments, with a bias towards tolerance. This is one reason why prototypes and related argument checking can be quite annoying. You expect that in the absence of a statment to the contrary that you can pass arguments as flattened lists (an argument against $$$ type prototypes), and that only the arguments relevent get used (an argument against caring about extra parameters). So for instance if a subroutine is documented that it takes a reference to a hash that contains the keys 'foo', 'bar', 'baz', 'bob' that it wont do anything horrible to or about the keys 'fred' and 'wilma'. And for me one expectation is that a subroutine won't use or complain about extra arguments that aren't needed or used. (Again this is more general observation, you've convinced me about the specific case.) I actually am saying this from a position of experience. THe POSIX::strftime() function and friends demands that you give them only the correct arguments. On a number of occasions I have had objects that comprised of an array(ref) made up of a format string and a localtime() output, with a number of other arguments following them in the array. I personally would have preferred (and attempted) to write

    my $timestr=strftime(@$self);
    and was annoyed at having to write
    my $timestr=POSIX::strftime(@{$self}[0..9]);
    It just seemed like an unnecessary step, particularly as the operation is just a cosmetic improvement over
    my @strftime=@{$self}[0..9]; my $timestr=POSIX::strftime(@strftime);

    I don't see the relevance, but I trust you that there is.

    Ok, well for me this comes under the subject of "interface usage restrcitions". I think that as a general rule an interface should be as flexible and forgiving as possible. And to me a warning is only slightly less annoying than a die(). Which is where I see the connection. Tenuous perhaps, but I did sort of miss your point. :-)

    Sometimes though, restriction can be accepted. One of those cases are when you see potential future constructions that might necessarily restrain certain things

    Hearty agreement for this paragraph. Although as a thought for another time I think that careful use of perls flexibility in argument handling at the initial interface design level can really cut down the number of changes to the literal interface. By this I mean something like using a hashref for options or even the use of key=>value pairs (named arguments) in parameter passing. Doing this means you usually can add attributes without fear of breaking peoples code. Incidentally if you delve through the older nodes here there are a number of discussions of argument handling tactics.

    Anyways, thanks for the reply. The next time I write something like my_lc() I just might stick a warning in there as you suggest.

    :-)

    --- demerphq
    my friends call me, usually because I'm late....

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (12)
As of 2014-12-18 13:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (51 votes), past polls