Re: if (ref $value eq "ARRAY") {
by Aristotle (Chancellor) on Aug 23, 2002 at 03:27 UTC
|
When I first started writing modules, I took a paranoid approach. The problem was, I had to get more and more paranoid as I discovered more and more variations of possible erroneous data input. My sanity checks turned more and more into insane checks.
After a while, I took a step back. This couldn't be it.
I thought about it and came to the conclusion that most of the time, if I'm given a totally wrong type of input (scalar when I expected a hashref, or something similar), it is a rare case that the program can recover gracefully. Most of the time it results in aborted execution one way or another. It may be a bit nicer if you give the caller a chance to clean up before he croaks, but essentially you can rarely deal with these errors in any other way than having the programmer fix the code, plain and simple.
Considering this, I decided to stick to semantic checks and leave the syntactical ones out. If I expect an arrayref, I may croak "not an arrayref" unless ref $param but won't go out of my way to ensure that it's really an arrayref. If I get a nonref and try to dereference it, Perl will die screaming. That's ok, we know something went wrong. I will however check whether scalar @$param reports the right number of elements, because that won't cause Perl to die screaming, it will simply make my code malfunction, leaving the user-programmer with no obvious clue about what went wrong.
The benefit is similar to all the other instances where Perl merely asks you to play by the rules rather than wielding a shotgun to force you to: I don't need to spend millenia coding sanity checks for every conceivable constellation under the sun, and in turn someone using my module can do something I didn't think of without having to spend millenia trying to trick my "sanity" checks.
Perl is laid back. Perl programmers should be laid back too. False paranoia is a waste of energy.
Makeshifts last the longest.
| [reply] |
|
I thought about it and came to the conclusion that most
of the time, if I'm given a totally wrong type of input
(scalar when I expected a hashref, or something similar),
it is a rare case that the program can recover gracefully.
Even if you could recover gracefully from wrong
input, it's probably a bad idea. If you get wrong input,
then somewhere along the line someone screwed up, and about
the worst thing you can do is hide the problem (or at least
pass it on to another chunk of code further away from the
original) and make it harder to debug. That sort of false
"defensive programming" isn't just a waste of time, it's
actively harmful.
In general, I like to write code that fails --
gracefully, mind you, not locking up the machine or any
such -- as soon as possible on bad assumptions. Makes it
easier to find bugs.
--
F
o
x
t
r
o
t
U
n
i
f
o
r
m
Found a typo in this node? /msg me
The hell with paco, vote for Erudil!
| [reply] |
|
| [reply] |
Re: if (ref $value eq "ARRAY") {
by blakem (Monsignor) on Aug 23, 2002 at 02:42 UTC
|
Not an answer to how much testing, but on what tests are available...
You might want to check out the reftype() and blessed() subs from Scalar::Util (core module as of 5.8)
They're designed to cleanly split the kludgy dual nature of ref() into two separate tests.
-Blake
| [reply] [d/l] [select] |
Re: if (ref $value eq "ARRAY") {
by dws (Chancellor) on Aug 23, 2002 at 02:50 UTC
|
But, I got to thinking. What if it's not really an array, but a class that looks like an array, or a tie? Should I use isa instead?
One of the downsides of a weekly-typed language is that you have to jump through lots of hoops if you want to protect yourself from every concievable ill that clients of your APIs can heap upon you.
So, how much testing should one do to see if arguments are what are expected, and how does "overloading" functions to understand different data types complicate matters?
Do as much testing as you need to, though if you find yourself using isa a lot, that's an indication that your interfaces need further thinking. A technique from Smalltalk, which I've seen used a bit in Java, is "double dispatch". It's a technique that, in part, lets you contain a geometric explosion in type testing when you introduce new types of objects. Look it up and study it. Double dispatch can be done in Perl.
| [reply] [d/l] |
|
I think some weakly-typed languages do better. To reflect back on the big thread from the other day that all languages evolve toward Lisp, Common Lisp allows for "overloading" functions based on type or arbitrary tests. So, one could separate things out into broad different things, but still be flexible within each thing.
I guess that's just syntactic sugar around the test I could write inside the function, now.
I think the concept here is that "all differences in allowed parameter types should be done through polymorphism". In Perl, that's hard to do with anything that's not blessed.
But, it does suggest something. I could have an abstract base class to serve as an "interface", and recognise that case as well as the built-in things (plain scalar and array ref). Then, anyone wanting to pass an object and have it mean something to this function would simply need to make it "isa" that interface class.
—
John
| [reply] |
Re: if (ref $value eq "ARRAY") {
by tstock (Curate) on Aug 23, 2002 at 02:43 UTC
|
if (UNIVERSAL::isa($value, 'ARRAY') {
Tiago
update: sorry about the hasty response... nothing to see here, move along now :) | [reply] [d/l] |
Re: if (ref $value eq "ARRAY") {
by particle (Vicar) on Aug 23, 2002 at 13:49 UTC
|
here's a little code to test a straight-up array ref, an object, and a tied array...
also, has it been blessed? might be of some interest to you.
#!/usr/bin/perl
use strict;
use warnings;
$|++;
package tiedarray;
use Tie::Array;
our @ISA=('Tie::StdArray');
package main;
for my $t_ref ( [], bless([]), tie(@{[]}, 'tiedarray') )
{
if( ref $t_ref )
{
print q{i'm a ref}, $/;
if( UNIVERSAL::can( $t_ref, 'isa' ) )
{
print q{i'm an object, too}, $/;
}
}
print $/;
}
__END__
# prints:
i'm a ref
i'm a ref
i'm an object, too
i'm a ref
i'm an object, too
i don't think it's too much to test two cases if( ref $t_ref and ! UNIVERSAL::can($t_ref, 'isa') ), given the benefits.
~Particle *accelerates*
| [reply] [d/l] [select] |
|
Hmm, so a tied ref is blessed automatically, also?
| [reply] |
Re: if (ref $value eq "ARRAY") {
by defyance (Curate) on Aug 23, 2002 at 02:54 UTC
|
This wont answer your question, its just some personal wisdom(not that I have much of that).
This is one of those "If I were in a simmilar situation" type things.
Say I was think of something that could be done to possible make the outcome of a situation better, since I've gone as far to think it up, I may as well go ahead and do it!
Unless, of course, your on a time constraint...
Nothing more to see here, move along
--~~--~~--~~--~~--~~--~~--~~--~~--~~--~~--~~--~~--~~--
perl -e '$a="3567"; $b=hex($a); printf("%2X\n",$a);'
| [reply] |
Re: if (ref $value eq "ARRAY") {
by fglock (Vicar) on Aug 23, 2002 at 12:11 UTC
|
use strict;
sub store_value {
my @range;
push @range, (ref $_[0] eq "ARRAY") ?
@{$_[0]} : @_;
print "range is ",join(",",@range),"\n";
}
store_value(1);
store_value(1..3);
store_value([1..3]);
If you add while (shift) { ... }
it will work even with (1, 2, [3, 4], 5) | [reply] [d/l] [select] |