Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

function call / return philosophies

by shemp (Deacon)
on Sep 03, 2004 at 18:49 UTC ( #388375=perlquestion: print w/replies, xml ) Need Help??

shemp has asked for the wisdom of the Perl Monks concerning the following question:

I have found myself writing a lot of little information transformation functions lately, for instance, we have a DB table that stores County names and state assigned County_ID's. So i have a function called county_name_by_id() that takes a db_handle, and a county_id as input, and returns the county name.

There are often occasions where i'd like to look up multiple county names from their ids at one time, so I allow the second param, county_id to be either a scalar (one county_id), or an arrayref of county_ids.

If called with one county id, it returns the name as a scalar, and if called with an arrayref, it returns a hashref where the keys are the county_id's and the values are the corresponding names.

This works fine, but I am determining the return context based on the input params, as opposed to using wantarray(). So this seems a bit paradoxical, because if i call the function with 1 county_id, I know I want 1 name back, and calling with multiple county_id's, I want multiple names back.

In any case, wantarray() wont tell me anything useful, becuase I'm returning either a string or a hashref, which get assigned to a scalar, so wantarray() will return defined but false in both cases if I call it with a proper return assignment:

my $name = county_name_by_id($dbh, 23); my $name_refs = county_name_by_id($dbh, [12, 25, 7, 13]);
So, is this type of call / return pairing a normal thing to do, or am I smoking crack today?

Replies are listed 'Best First'.
Re: function call / return philosophies
by Eimi Metamorphoumai (Deacon) on Sep 03, 2004 at 18:56 UTC
    What I've seen most often is a function that takes a list of, for instance, county_id's, and returns a list of names in the same order. Then if you pass in one or 20, or even none at all, it does essentially the same thing, and you don't need special processing on either end to deal with different calling conventions. If you really want the hash, you can make it on the calling side.
    my %names; @names{@county_ids}=county_name_by_id($dbh, @county_ids);
Re: function call / return philosophies
by ehdonhon (Curate) on Sep 03, 2004 at 19:03 UTC

    Varying return types has always caused problem for me down the road. Best thing to do might be to always return ref regardless of the number of parameters you receive.

    I think a hashref here is a better return value than an array ref. Perl already has hashes built in. If you return an array ref, you are forcing the user of your API to re-implement associative arrays.

Re: function call / return philosophies
by ikegami (Patriarch) on Sep 03, 2004 at 18:59 UTC

    Why not? Put that example in the docs, and it should be quite clear to the reader.

    If you want to avoid any confusion, create an alias such that the calling convention becomes:

    my $name = county_name_by_id($dbh, 23); my $name_refs = county_names_by_ids($dbh, [12, 25, 7, 13]);

    You don't have to advertise it's really the same function.

Re: function call / return philosophies
by ccn (Vicar) on Sep 03, 2004 at 19:56 UTC

    I like how DBI handles that:

    @row_ary = $sth->fetchrow_array; $first_val = $sth->fetchrow_array; $ary_ref = $sth->fetchrow_arrayref;
Re: function call / return philosophies
by acomjean (Sexton) on Sep 03, 2004 at 20:17 UTC
    In most languages that allow this kinda of thing its called "overloading" the function. It can behave differently based on what input parameters. Some really stronly typed languages (ada) allow overloading on input and output parameters.

    It can be usefull but also add to confusion and maintenance issues.

    I would sugest as someone else has always return the same type.

    Or better in my opinion have 2 functions (maybe add county_names_by_ids()) Of course this new routine could just call the orginal and return a populated array saving code duplication. It would be pretty much the same code you have now just in 2 routines.

Re: function call / return philosophies
by ihb (Deacon) on Sep 03, 2004 at 21:31 UTC

    I would probably not do it the way you've done it, from a user point of view.

    To keep it simple and not over-implement, I usually design things like these to work as &foo below.

    sub foo { my @names = @_; ... ; return @values if wantarray; return $values[0]; }
    Even thought the implementation is explicit, I'd leave the behaviour for a "list call" in scalar context undefined in the documentation.

    If I'd want to have it as a hash I could do

    my @ids = ...; my %foo; @foo{@ids} = foo(@ids);
    or use my utility function &zip
    sub zip { my ($x, $y) = @_; map { $x->[$_], $y->[$_] } 0 .. max($#$x, $#$y); } my @ids = ... ; my %foo = zip(\@ids => [ foo(@ids) ]);
    which is good when you don't want to use an intermediate variable.

    A lot of the time I don't care to handle more than one argument though, because I often use map for that

    my @ids = ... ; my @foo = map foo($_) => @ids; my %foo = map { $_ => foo($_) } @ids;
    But I don't know enough about how you'll use this function in practice to say anything about how you should do in your code. In particular, you might want to handle list calls because the DB access might benefit from that.

    Hope I've helped,

    Read argumentation in its context!

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://388375]
Approved by Eimi Metamorphoumai
Front-paged by Old_Gray_Bear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2022-05-19 11:16 GMT
Find Nodes?
    Voting Booth?
    Do you prefer to work remotely?

    Results (71 votes). Check out past polls.