Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic

How do I check if a scalar in @_ is writable?

by beppu (Hermit)
on Oct 27, 2003 at 03:47 UTC ( #302323=perlquestion: print w/replies, xml ) Need Help??

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

Contrived Example:
#!/usr/bin/perl -w use strict; use warnings; use HTML::Entities; sub sanitize { # no name? $_[0] ||= "anonymous"; # add missing protocol in $url if ($_[1] && $_[1] !~ /^\w+:/) { $_[1] = "http://" . $_[1] } # add missing protocol in link in $body $_[2] =~ s{\[(.*?)\]}{ my $x = $1; if ($1 !~ /^\w+:/) { if ($1 =~ /\@/) { "[mailto:$x|$x]"; } else { "[http://$x]" } } else { "[$x]" } }gex; # don't let people put in html $_[2] = HTML::Entities::encode($_[2]); } my $body = "Mail me at [bugs\]."; my $url; my $name; sanitize("", $url, $body); print $body, "\n";
Running this script will yield this error:
Modification of a read-only value attempted at line 9.
To prevent this error, how would I check to see if $_[0] is a scalar that I can write to? Thanks in advance. :)

Replies are listed 'Best First'.
Re: How do I check if a scalar in @_ is writable?
by PodMaster (Abbot) on Oct 27, 2003 at 04:04 UTC
    You don't need to check (you could use Scalar::Util if you did, or eval)
    $_[0] ||= "anonymous"; # <<<<<<<<<<<<<<<<<<< wrong wrong wrong
    The intent of your function is to modify $body (the 3rd argument), so restrict yourself to modifying only $body. It is (imho) a better idea to do it like
    sub roy { my( $f, $b, $modifythisREF ) = @_; $$modifythisREF =~ s/yoda/yoda/g; return; } roy( "", "", \$body ); ...

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Re: How do I check if a scalar in @_ is writable?
by theorbtwo (Prior) on Oct 27, 2003 at 06:46 UTC

    You can see if a scalar is read-only by using Scalar::Util::readonly. (That's the same as the Util routine several other people have mentioned, but the module was renamed, and now comes with recent versions of perl.)

    However, I think you API is rather poor, and changing the API would relieve the need to check for read-only parameters. In general, you should only modify your parameters when you have /very/ good reason, because it isn't what your users expect.

    Instead of taking in a name, a url, and a body, and refering them soley by their entries in @_, copy them into variables in your subroutine, and return the modified body.

    Not only does this make your function behave the way callers are (more likely to) expect it to work, it makes the body of your function read more clearly. I noticed you refer to your paramters in the comments as "name", $url, and $body. So, taking from your lead...

    #!/usr/bin/perl -w use strict; use warnings; use HTML::Entities; sub sanitize { my($name, $url, $body) = @_; # no name? $name ||= "anonymous"; # add missing protocol in $url if ($url && $url !~ /^\w+:/) { $url = "http://" . $url } # add missing protocol in link in $body $body =~ s{\[(.*?)\]}{ my $x = $1; if ($1 !~ /^\w+:/) { if ($1 =~ /\@/) { "[mailto:$x|$x]"; } else { "[http://$x]" } } else { "[$x]" } }gex; # don't let people put in html $body = HTML::Entities::encode($body); # Return modified body. return $body; } my $body = "Mail me at [bugs\]."; my $url; my $name; $body = sanitize("", $url, $body); print $body, "\n";

    Now, the varibles that are not assigned to in the mainline of the program aren't modified. You can pass in whatever you want in $name, without worring about if it's writable, because your copy of it will be. The API of your sub is more like it's users probably expect.

    If you really want to not change the API of your function, so that you don't have to modifiy the existing callers, you can, near the return line, check for not defined wantarray, that is, if your sub is being called in void context (it's return value isn't being assigned anywhere), and do a $_[2]=$body.

    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      Yeah, I admit that the API is shitty, but I was just amusing myself with @_ aliasing when I found this error. I was the only user of this little subroutine, anyway, and it wasn't part of any library I was going to distribute or anything.

      Thanks for the tip on Scalar::Util::readonly(), and thanks to everyone else for their comments.

Re: How do I check if a scalar in @_ is writable?
by tachyon (Chancellor) on Oct 27, 2003 at 04:06 UTC

    Try/catch with eval would work but I don't really see why you need to operate on $_[0] .....

    eval{$_[0] ||= 'blah'} if ( $@ ) { # handle exception.... print "blech!\n"; } # else .....




Re: How do I check if a scalar in @_ is writable?
by Zaxo (Archbishop) on Oct 27, 2003 at 04:10 UTC

    You can trap errors with eval,

    eval { $_[0] ||= 'anonymous' }; return if $@;
    where the eval braces are used to precompile the expression.

    Since these errors are really from misusing the sanitize() API, perhaps it would be better to perl prototype it, to convert the error to compile-time. A prototype of (\$\$\$) would work for the way you have the function written.

    There is no need, however, for you to act on variables outside the function. If you just start the sub with     my ($user, $url, $body) = @_; and write the remainder on those variables, then you are guaranteed that all three are writable and no testing is required.

    After Compline,

Re: How do I check if a scalar in @_ is writable?
by batkins (Chaplain) on Oct 27, 2003 at 04:08 UTC
    I don't know how to answer your actual quesiton, but I think your approach is rather strange. Personally, I'd pass references into the subroutine and have the sub modify their referents.

    Just a thought.

    The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows. - Frank Zappa

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (4)
As of 2021-09-23 00:34 GMT
Find Nodes?
    Voting Booth?

    No recent polls found