Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Returning string and numerical data types from subroutines

by saurabh.hirani (Beadle)
on May 15, 2009 at 15:57 UTC ( [id://764298]=perlquestion: print w/replies, xml ) Need Help??

saurabh.hirani has asked for the wisdom of the Perl Monks concerning the following question:

Hi guys,

I am writing a subroutine for daemonizing a process. Broadly speaking I've written it like this:

sub daemonize { # try to daemonize if (success) { return 1 } else { return errstr } }

I call it this way

$isdaemon = daemonize() if ($isdaemon ne "1") { print "error: $isdaemon"; }

The problem with this is that the caller needs to do a "ne" check as it works for both 1 and error string. I would like to change the calling interface to:

if (! daemonize) { report error }

But in the current implementation I can't use the desired way because if daemonize fails it returns an errstr which passes the "if (! daemonize)" test. I want to report the error, so I cannot return 0 on failure.

I wanted to set the errstr in a way that $! does it - which is make the subroutine call more intuitive, like the way we use open or close

open($fh, file) or die "$!";

The best I can do is this:

use Scalar::Util; sub daemonize { # try to daemonize if (success) { return 1 } else { return dualvar 0, errstr } }

so the usage is

$isdaemon = daemonize; if ($isdaemon == 0) { print "error: $isdaemon"; }

which is better than the first way because I can use $isdaemon is both the string and numeric context and get my work done, but it still isn't as good as the way $! does it.

  1. Is there a better way?
  2. Can I not set $! and use my subroutines like I have used open() call above? A perl DBI module sets $DBI::err variable. I may do that but that's plan B.
  3. How do you guys handle such situations where in the return value is more than success or error - like a subroutine which checks if lockfile is in use or not - return 0 if lockfile free, return pid if someone using the lockfile, return error if failed to check if someone is using it?

Replies are listed 'Best First'.
Re: Returning string and numerical data types from subroutines
by ikegami (Patriarch) on May 15, 2009 at 17:03 UTC
    Two common mechanisms:
    • sub daemonize { action() or die "$errstr\n"; return 1; } eval { demonize() } or print("error: $@");
    • # A global could also be used instead of an attribute. sub daemonize { action() or return $self->set_error($errstr); return 1; } $o->demonize() or print("error: " . $o->get_error());
Re: Returning string and numerical data types from subroutines
by mikeraz (Friar) on May 15, 2009 at 18:18 UTC

    You may benefit from some time with Perl Best Practices. The chapter on Error Handling addresses this issue.

    Specifically, as two other posters suggested, throw an exception rather than reply on the caller to check the return value. This approach would address the issue you have with interpreting the return code while also creating more robust (where robust is works as you intend and fails at the right time) code.


    Be Appropriate && Follow Your Curiosity
Re: Returning string and numerical data types from subroutines
by gwadej (Chaplain) on May 15, 2009 at 16:53 UTC

    As an alternative approach, using die to throw an exception on failure can get you out of this problem.

    Now, daemonize doesn't need to return anything. An eval block and test of $@ covers the error case. Any code immediately after the daemonize call is in the success case.

    Of course, you can replace the die/eval with one of the exception modules.

    Many may argue whether this is a better solution or not, but it would avoid your current problem.

    G. Wade
Re: Returning string and numerical data types from subroutines
by afoken (Chancellor) on May 15, 2009 at 20:36 UTC

    Not completely unrelated: Rely on the system environment to put your program into daemon mode, don't attempt to re-invent the wheel for the millonth time. Daemontools can make a daemon from almost any program. Reliable logging is also handled by daemontools, just write to STDERR. See also The DJB Way.

    From the viewpoint of a library user, I would prefer the principle of the least surprise:

    • A function should return a false value on error (preferably even in list context) and a true value on success, for the good old my $foo=function() or die "Can't function: $!";. Assigning to $! should do no harm, even if it is just a string.
    • A function that knows that some fatal error has occured (memory exhausted, internal structures damaged, unrecoverable system call errors) should better die(). If I (as a library user) really know better, I can always wrap the function call in an eval { BLOCK }.
    • A function returning a string should not add unexpected behaviour to that string. If you want to return an error string, behave like system and evaluate to false on SUCCESS, so a user can write error handling à la C: my $err=function(); if ($err) { die $err }. I don't like that style very much, because it requires manual error checking (remember: we have die and eval { BLOCK }) and blocks the return value for better uses.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      Thank you all for your feedback. I was avoiding the eval approach because I didn't want the program to die if the subroutine call fails. But then, as most of the posts suggested - its better to take matters in your own hands than to trust the library user with error checking and handling. Makes sense. I will keep that in mind.

Re: Returning string and numerical data types from subroutines
by Fletch (Bishop) on May 15, 2009 at 16:27 UTC

    There's dualvar in Scalar::Util which lets you make your own values that behave like $! which might be what you want.

    Update: Duurr but you know that. Teach me to post while distracted.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      It seems the OP is already aware of dualvar.

Re: Returning string and numerical data types from subroutines
by John M. Dlugosz (Monsignor) on May 15, 2009 at 20:30 UTC
    return "some error string" but false;
    Er, not in Perl 5. There's some kind of "property" module, but I don't think you can make it interact with the bool tests.

    I like using die for failure, return status code for OK.

    Or, package up your different kinds of values into a class and always return an instance of that. So, you can have your string and still be false, after all!

    —John

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (3)
As of 2024-04-24 22:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found