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

API design - if you're expecting a scalar and an array ....

by metaperl (Curate)
on May 22, 2009 at 15:08 UTC ( [id://765697]=perlmeditation: print w/replies, xml ) Need Help??

If you look at the API of XML::Writer you will see that to create a tag, you make a call like this:
$writer->startTag('doc', 'version' => '1.0', 'status' => 'draft', 'topic' => 'AT&T');
Notice that you pass a single flat list, even though there are two semantic elements being passed - the tag name and the tag's attributes.

In XML::Writer::Nest, I decided to break with this convention (even though the current CPAN version honors it). The reason why

  1. You get the tag name visually distinct from its attributes...
  2. Your API clearly separates arguments that are separate
  3. Passing an array reference is more efficient than pass by value? Help me out here..
  4. Easier to extend. If you decide you need an additional parameter to this sub, then one more positional parm is easy if each original parm is just a scalar. Otherwise, you have to start popping values off the end of @_ and other things to get at extra values.
So, with the soon-to-be-uploaded version of XML::Writer::Nest, you will write:
{ my $nest = XML::Writer::Nest->(tag => 'level1'); { $nest->nest(newlevel => [attr1 => 1, attr2 => 2]); ... } }
instead of
{ my $nest = XML::Writer::Nest->(tag => 'level1'); { $nest->nest(newlevel , attr1 => 1, attr2 => 2); ... } }

Replies are listed 'Best First'.
Re: API design - if you're expecting a scalar and an array ....
by LanX (Saint) on May 22, 2009 at 20:12 UTC
    Sorry I'm confused, why do you pass named parameters as a arr-ref instead of a hash-ref?

      $nest->nest(newlevel => [attr1 => 1, attr2 => 2]);

    instead of

     $nest->nest(newlevel => {attr1 => 1, attr2 => 2});

    To your topic, I think it's good practice to use a literal hash-ref for named parameters when your mixing with positional parameters (or expect to do so later), like in this example.

    Cheers Rolf

Re: API design - if you're expecting a scalar and an array ....
by eyepopslikeamosquito (Archbishop) on May 23, 2009 at 00:35 UTC

    I'd say XML::Writer got it wrong and should have used a hash reference rather than a flattened list. Note that Perl Best Practices, page 182, advises you to pass a hash ref, not a list of name/value pairs, and so allow you to catch some errors at compile time, rather than run time. This point, along with many others related to API design, is dicussed in detail in On Interfaces and APIs.

      I'm not sure that using a hashref for XML attributes is automatically a good idea. You might want to be able to check the list for duplicates and warn about that, rather than silently discarding all but the last instance.

      Frankly this is one instance where PBP is on shaky ground. You don't actually get the error at compile time, and you don't get it reliably at all. You really aren't gaining much.

Re: API design - if you're expecting a scalar and an array ....
by John M. Dlugosz (Monsignor) on May 22, 2009 at 16:22 UTC
    If the list is specifically XML attributes to put in the element, rather than general named parameters to the function, I agree with points 2 and 4.

    I don't know about the style of using the fat arrow though. Is that whole thing supposed to be a logical "pair"? Or just two top-level parameters? If you add a 3rd, the use of => won't be sensible anymore, so you are ignoring point 4.

    —John

Re: API design - if you're expecting a scalar and an array ....
by metaperl (Curate) on May 22, 2009 at 15:15 UTC
    Actually one drawback to use array references is apparent. You get parentheses-itis when trying to close your expression, because you cant just keep hitting close-parenthesis, but have to know when to hit the close-bracket instead.

    Look at the calvalcade of closing characters in this single line

    # new style, more bracing, more types of bracing, harder to know wha +t to close { my $nest = $o->nest( 'PolicyCoverage', [CurrentlyInsured => Local: +:Util::torf($L->{'insured'})]); # old style - fewer levels of bracing, and fewer types of bracing { my $nest = $o->nest( 'PolicyCoverage', CurrentlyInsured => Local: +:Util::torf($L->{'insured'})) ;
Re: API design - if you're expecting a scalar and an array ....
by JavaFan (Canon) on Jun 01, 2009 at 23:53 UTC
    1. You get the tag name visually distinct from its attributes...
    You do? Just because the second token after the tag name is now a [? IMO, you get far better visual distinction by using whitespace. For instance:
    $nest->nest('newlevel', attr1 => 1, attr2 => 2, attr3 => 3);
    2. Your API clearly separates arguments that are separate
    By that argument, we ought to write
    printf "%d", [3.4];
    3. Passing an array reference is more efficient than pass by value? Help me out here..
    That depends. Certainly, pushing one reference on the stack is cheaper that pushing 96 key value pairs. But if you first have to create an array with those 96 key value pairs for the sole purpose of handing the sub a reference which is then has to dereference, all the gain is lost. Unless you know more about the calling code, you cannot say much about the overall gain or loss when using arrayrefs.
    4. Easier to extend.
    Maybe. Does a possible future extension outweight the negatives of code that now needs to change?

    Note that by sacrificing point 4 you can get points 1, 2, 3 while remaining backwards compatible by doing something like:

    sub nest { my $tagname = $_[0]; my $attributes = @_ == 2 ? $_[1] : [@_[1 .. $#_]]; ... }
    Instead of forcing your ideas of API clearity and visual distinction of the tag name upon the users of your code, you leave it to the users to decide whether they find this important or not.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (2)
As of 2024-04-25 19:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found