Subject: | Please prefer "INTEGER" over "FLOAT" |
Boolean values are considered of type FLOAT, which is likely surprising
to many. Also, some numbers that are considered FLOAT would be better
to consider as INTEGER.
(I was curious whether Boolean values would be treated as STRING or
INTEGER so I tested and was reminded that PL_sv_no and PL_sv_yes have
not just PV and IV pre-filled, but also NV -- I also tested $! and found
it of type STRING, which seems best).
I wasn't surprised that autobox chose to pay attention to SV_NOK before
the PV and IV equivalents (I presume). But the result in the case of
Boolean values is unfortunate. And after some extensive experimenting,
I think autobox should actually look at SV_IOK before SV_NOK.
First, since so many built-ins return Boolean values (and I work with a
lot of code that explicitly Booleanizes values via !!), it seemed
reasonable to consider autobox trying to special-case them. I'd be fine
with type(!!0) and type(!!1) returning "INTEGER" but would probably
prefer a new type of "BOOL" just for them.
Unfortunately, PL_sv_no and PL_sv_yes mostly get copied so the only way
to detect a "Boolean value" would be to look at the values stored in the
SV. This can be done tolerably well for PL_sv_no. Unfortunately, a
copy of PL_sv_yes looks exactly like the following quite unBoolean SVs:
#!/usr/bin/perl -wl
use strict;
use autobox::universal 'type';
use Devel::Peek 'Dump';
my $s= '';
my $i= 1;
print type($i); # INTEGER
if( $i*1.5 == 1.5 ) {
$s .= "$i is 1.0";
}
# Now $i is "identical" to a copy of PL_sv_yes
Dump( $i );
my $f= 1.0;
print type($f); # FLOAT
if( $f & 1 ) {
$s .= "odd $f";
}
# Now $f is "identical" to a copy of PL_sv_yes
Dump( $f );
my $true= !0;
Dump( $true );
While experimenting with lots of cases related to this, I came to
realize that looking at SV_IOK before SV_NOK probably produces better
results.
First, a tangential case to warm up:
#!/usr/bin/perl -wl
use strict;
use autobox::universal 'type';
my $s= 'string';
print type($s); # STRING (good)
print type(0+$s); # INTEGER (good)
print type($s); # FLOAT (ugh)
It seems like a (long-standing) bug that Perl sets SvIOK/SvNOK (not just
SvIOKp/SvNOKp) in the above case, but autobox.pm can't really do
anything about that (other than file a bug against Perl and hope). It
is my expectation that Perl should only set SvIOK and/or SvPOK if $s
looks_like_number() of the appropriate type.
Or, perhaps autobox could have an option to check looks_like_number()
when given an SV with both SvPOK and ( SvIOK or SvNOK ) where the
distinction would matter? Maybe it could even unset SvPOK/SvIOK if it
doesn't looks_like_number() to save time the next time. Well, I'd
rather "fix" Perl (but expect eventually some reason to surface as to
why things are this way, despite not being able to find one after
consulting some true experts).
Now let's hypothesize that I defined new_type() that acts how I'd like
autobox's type() to behave (it checks SvIOK before SvNOK).
#!/usr/bin/perl -wl
use strict;
use autobox::universal 'type';
use Devel::Peek 'Dump';
my $f= 1.5;
print type($f); # FLOAT (good)
print type(0&$f); # INTEGER (good)
print type($f); # FLOAT (good)
Dump( $f );
# Note that IOK is *not* set, just pIOK
# new_type($f) would still be "FLOAT" (good)
#!/usr/bin/perl -wl
use strict;
use autobox::universal 'type';
use Devel::Peek 'Dump';
my $f= 1.0;
print type($f); # FLOAT (good)
print type(0&$f); # INTEGER (good)
print type($f); # FLOAT (fine)
Dump( $f );
# Note that IOK *is* set, not just pIOK
# new_type($f) would be "INTEGER", which seems okay
#!/usr/bin/perl -wl
use strict;
use autobox::universal 'type';
use Devel::Peek 'Dump';
my $i= 1;
print type($i); # INTEGER (good)
print type(int($i/3)); # INTEGER (fine)
print type($i); # FLOAT (Hrm!)
Dump( $i );
# new_type($f) would be "INTEGER", which seems better
So needing the integer part of an NV wouldn't make my proposed version
of autobox treat the number as an INTEGER unless the NV has no
fractional part. But doing a floating-point computation with an IV
makes the current version of autobox treat it as a FLOAT from then on
(footnote). I was a bit surprised how easy it was to get Perl to
compute the NV for an IV.
(footnote: Unless the IV is too large to fit with full precision in the
NV, which can happen with 64-bit IVs if NVs aren't "long double".)
And looking at SvIOK first would also make Boolean values INTEGER, which
seems acceptable given how they are closest to the values 0 and 1 (and
defining a "BOOL" type seems doomed).
Thank you.