Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^2: How should named paramater keys be named?

by DeadPoet (Scribe)
on Jul 02, 2011 at 01:32 UTC ( #912425=note: print w/ replies, xml ) Need Help??


in reply to Re: How should named paramater keys be named?
in thread How should named paramater keys be named?

Just my 2 cents on the matter.

First, I love named parameters, but use both styles -- named and unnamed. It all just boils down to the level of confidence that you have in the values, and respective ordering, that have been passed. If it is an internal function, method, subroutine, whatever that you are certain of the parameters, then named parameters are not really needed. However, if it is an interface exposed to the public where you are not certain of the ordering, then named parameters may be in order.

Next, there is no substitute for checking what is passed. Unexpected, or bad, programmatic behaviour happens when parameters and return codes are not checked. Never assume the sky is always blue.

Finally, BrowserUk is correct in saying that all the bits add up. One needs to take in to context what is being done and who is doing it. Memory footprint, CPU usage, and overall responsiveness of an application is just as important as what the application accomplishes. Too often one comes across a "Swiss army knife" application that does everything but does nothing well because of its' design. Personally I would rather have an application that does one thing really well than an application that tries to do many things and is hard to maintain.

Like I said, just my two cents.

--Poet


Comment on Re^2: How should named paramater keys be named?
Re^3: How should named paramater keys be named?
by BrowserUk (Pope) on Jul 02, 2011 at 07:34 UTC
    However, if it is an interface exposed to the public where you are not certain of the ordering, then named parameters may be in order.

    You really think that remembering the ordering of parameters is harder than remembering the names, spelling, casing, presence or absence of leading hyphens etc?

    Do you you use (or just know of) a non-constructor api that you feel really benefits from the use of named parameters?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      The LWP::UserAgent/WWW::Mechanize API (and especially the WWW::Mechanize::Firefox API) has lots (and lots) of optional parameters, which are mostly filled in as defaults from the object when left out or left alone. The first such example in WWW::Mechanize::Firefox is

      $mech->progress_listener( $source, %callbacks )

      ... where you specify the callbacks by name instead of by position, as usually, you want only to set one or two callbacks.

      The same counts for the ->get() method:

      $mech->get( $url, %options )

      Here, the options can specify a target filename where to store the response, whether to bypass the cache etc.. All of these parameters are fairly orthogonal and it wouldn't make much sense to have them positional in my opinion.

      Two APIs that constantly trip me up is the DateTime API and the Imager::Color API. The DateTime API wants me to always specify named parameters when I'm calling any function even though the method only allows one parameter and makes sense for only one parameter. The Imager::Color API raises no warning for:

      my $blue = Imager::Color->new( 0, 0, 255 );

      ... but creates no sensible object either. It wants

      my $blue = Imager::Color->new( r => 255, g => 0, b => 0 );
        where you specify the callbacks by name instead of by position, as usually, you want only to set one or two callbacks.

        Optional arguments are the exception that proves the rule. (IMO :)

        The DateTime API wants me to always specify named parameters when I'm calling any function even though the method only allows one parameter and makes sense for only one parameter.

        And that demonstrates the problem with guidelines. There will always be those who apply them religiously without thinking. Cargo-culting of the very worst kind.

        Another that bugs me is q().

        The Imager::Color API raises no warning for: ... but creates no sensible object either.

        I liked the SmallTalk approach to the problem where the names of arguments to methods are effectively a part of the method identifier. Eg.

        pen: anInteger for: aHandle medium: aGraphicsMedium

        The best bit about this is that the parameter names are checked at compile time.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        That behaviour of Imager::Color would be a bug, given the documentation.

        But I can't reproduce it:

        tony@mars:.../git/bse$ perl -MImager -le '$c = Imager::Color->new(0, 0 +, 255); print join ",", $c->rgba' 0,0,255,255

      Once again it depends on the complexity of the parameters that are being supplied. One, two, three parameters exposed to the public maybe not an issue, and agreeably named parameters probably not needed.

      However, if the interface in to the whatever you are calling requires a more complicated set up, then I would say that named parameters are in order. Now with that said, when using named parameters they better be consistent throughout the entire program. Moreover, I would go so far as to say they should also be lower case and clearly documented. Personally I see named parameters having value, but the value is only obtained where exposed to the public. Code that is on the back end that users cannot call directly why would they be needed--you already have established programmatic control over the parameters.

      Yes, I use named parameters outside of a constructor -- my previous restrictions still apply ( internal NO, user called YES ). An example would be when I want to enable a local debug. Keep in mind that the routine already has other values being passed.

      sub foo { my $o = { q{debug} => 0, ## debug 0|1. q{db} => SQL_DB, ## sql db. q{host} => SQL_SERVER, ## sql server. q{port} => SQL_PORT, ## sql server port. q{user} => SQL_USER, ## sql user. q{pass} => SQL_PASS, ## sql user pass. @_ }; ## do your safety checks... my $dsn = q{DBI:mysql:database=} . $o->{'db'} . q{;host=} . $o->{'host'} . q{;port=} . $o->{'port'}; my $objSQL = eval { DBI->connect( $dsn, $o->{'user'}, $o->{'pass'}, { 'PrintError' => 0, 'RaiseError' => 0 } ); }; if ( ( $@ ) || ( ! defined $objSQL ) ) { ## maybe we only want to show the error if in debug?? print STDERR $DBI::errstr if ( $o->{'debug'} ); return undef; } DBI->install_driver( q{mysql} ); return $objSQL; }

      Now, I did not run check the code above, and it is ONLY an example of what can be done. Also, I do not expect everyone to agree with the code. So, not only does this allow a user to call the function, method, subroutine, etc... but it provides simple named parameters by which it can be called. More importantly, it allows the end user to call it without having to care about the order and if the parameter is not specified then it is defaulted. I understand that one cannot always default to a meaningful value, but this is just an example. As far as my style goes, I do not like the leading hyphen, nor do I like the mixed case, such as the DBI Connect implements. But that is just my style. Honestly, I prefer the shorter single case format.

      Finally, named parameters have their place and can provide value. The trick is to balance how and when they are used. Is there a cost? Yes. Can they provide a more robust public interface? Yes. Can they be overused? You better believe it.

        The problem with your arguments is that they are based upon emotion rather than logic. Your preferences rather than clear reasoning.

        1. You say "I see named parameters having value, but the value is only obtained where exposed to the public.", but then go on to say "I prefer the shorter single case format.".

          But the public interface is for other people to call, not you, so surely you should cater to their preferences, not yours?

          But of course you couldn't possibly cater to all their preferences, no more than they are all likely to cater for your preferences.

          So the problem remains that even if you could guess what the names of the arguments might be (highly unlikely), for some new module you are using, you'd still have to look up the documentation to discover the casing of those names. And as soon as you have to look the up, any "easier to learn" justifiction for named arguments goes right out the window.

          And once you've looked it up, the ordering of parameters are far easier to remember than the names and casing, so the "easier to remember" justifiction crashes and burns also.

          All that leaves is the nebulous old standby, the "easier to read" justifiction. But that doesn't stand up to logical analysis either.

          Take your 'foo' function, as poor an example as it is. The crux of your defence is that this;

          my $dbh = foo( q{debug} => 0, q{db} => SQL_DB, q{host} => SQL_SERVER, q{port} => SQL_PORT, q{user} => SQL_USER, q{pass} => SQL_PASS, );

          Is somehow easier to read than:

          my $dbh = foo( SQL_SERVER, SQL_PORT, SQL_DB, SQL_USER, SQL_PASS, DEBUG + );

          Note the reordering of the parameters into their 'natural ordering'. Ie. A server responds on a port, contains the DB which can be accessed by a user with an appropriate password, who may want to debug this code.

          And that just ain't true. Regardless of whether you are passing constants (symbolic or inlined) or variables, the names of the variables or symbolic constants, or the values of the inlined constants makes the naming of the parameters just redundant noise. (And ignoring the quoting properties of the fat comma by using 'fat quoting' is just more redundant noise.)

          Far better and simpler would be:

          my $dbh = foo( SQL_DSN, SQL_USER, SQL_PASS, DEBUG );

          There is no point in breaking out the DSN sub-fields as there is nothing you can do to validate them. Can you check the name of the server or database or user? Tell the guy he has supplied the wrong password? I guess you could check that the supplied port number is in the range 0..65535, but then your users might be using a named pipe connection anyway.

          And defaulting them individually makes no sense at all. You've said this is a public API, so what are you going to use as the default for the server name (for all your users!): MySqlServer? The DB name: myDB? The user name:root? Password?

          For a more thorough examination of the futility of breaking apart DBI DSNs see Re^5: Avoiding compound data in software and system design.

          Even port number, unless this is an open DB, security suggests that it makes a lot of sense to not use the default port number. No point in making it easy for the hackers.

        2. Your example is very badly chosen.

          Other than a lip-service comment, you've completely ignored a major part of my argument, that of parameter checking.

          Named parameters hugely complicate the process of parameter checking. So much so, that it almost guarantees that your token comment will remain that way ever more.

        3. Your example is badly implemented.

          In addition to all the overhead created by the parsing and validating of named parameters, (re)constructing your defaults hash every time you call the function is almost criminal.

        4. The fact that your example is contrived for the purposes of supporting your emotive argument, suggests that you didn't have a real example somewhere in your codebase. And that further suggests that this is an idea you subscribe to, not a practice that you er... practice.

          And ideas born of purely theoretical, high ideology, are rarely practical in practice. They become burdens to ingenuity and productivity for no payback.

          I was reading an article the other day about a bunch of people and companies trying to come up with standards for cloud computing. The crux of piece is that as yet, no one really knows what "cloud computing" is, never mind the best way to do it. It is therefore way too premature to be considering draughting a standard, much less to impose it upon anyone.

          The best (I would say:only good) standards are the de-facto standards that get codified, cleaned up and adopted. You have a free-for-all period where everyone tries to do things whatever way seems to work, and slowly, over a period of practical usage, certain practices fall out as being easier to use, or more effective or more efficient. Ie. More practical. That's when standardisation should be considered.

          IMO the biggest problem with CPAN, is that the APIs for many of the modules there where defined on the basis of the author sitting down and inventing them, rather than them falling out of writing an application that needs to do what the module does. Which means that applications end up having to be structured to fit the exposed API, rather than the API fitting the way you would want to write the application. (There are also many good exceptions to that!)

        There *is* utility in using named parameters for sub/methods that (usefully) take large numbers of optional parameters. And that mostly means constructors.

        There are examples of non-constructor functions/methods that could have many optional parameters--fetching a url (get) is one such with all the possibilities of headers, agent ids, cookies, redirects, time-outs et al. But even then there are good ways and bad ways of defining the API. Most of the options used by get() for any given application are unlikely to change for every fetch. So, whilst the run-time of a fetch is likely to always be dominated by the IO, it simply doesn't make sense to re-parse and re-validate those options for every individual fetch, so using the options to construct a user agent, and then have the $ua->get( $url ) as a simple method of the user agent is just good design.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2014-07-31 22:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (254 votes), past polls