Perl’s basic data type is the scalar, which takes its name from the mathematical term for “single item”. However, the scalar is really two things. You probably know that a scalar can be either a number or a string, or a number that looks the same as its string, or a string that can be a number. What you probably don’t know is that a scalar can be two separate and unrelated values at the same time, making it a dualvar.
You’re already using a dualvar without knowing it. The $!
variable, which holds the value of the last system error, is most often used in its string form:
open my $fh, '>', $filename or die "Error: $!";
If something goes wrong with that open
, you’ll get an error such as these:
No such file or directory Permission denied
Both of those error messages have numbers associated with them. You can output their numeric value
open my $fh, '>', $filename or die $! + 0;
Now the errors are numbers, which correspond to the errno
value for the system call:
2 13
These numbers are keys in the %!
, and the value for that key is true if that was the last system error.
Perl, on its own, doesn’t give you a way to create this sort of variable yourself. When you assign a new value to a scalar, whether string or number, Perl clears the previous values it has. When you use a number as a string, Perl converts it to a string, and the same the other way around.
You can watch this with Devel::Peek. Here’s a program that sets a string value:
use Devel::Peek; my $value = 'abc'; Dump( $value );
In the scalar record, the POK
flag is set, indicating the variable has a string form, and the PV
slot has a value (see perlguts for more details):
SV = PV(0x100801070) at 0x100827810 REFCNT = 1 FLAGS = (PADMY,POK,pPOK) PV = 0x100202870 "abc"\0 CUR = 3 LEN = 16
If you set a numeric value, the flags are different:
use Devel::Peek; my $value = 137; Dump( $value );
Now there’s an IOK
flag, and one of the numeric slots, in this case the IV
, is set:
SV = IV(0x100827800) at 0x100827810 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 137
However, if you take the numeric value and use it in a string context, Perl also creates a string version. Now the scalar has both the IOK
and POK
flags, and both the IV
and PV
slots have values:
137 SV = PVIV(0x100809208) at 0x100827840 REFCNT = 1 FLAGS = (PADMY,IOK,POK,pIOK,pPOK) IV = 137 PV = 0x100202870 "137"\0 CUR = 3 LEN = 16
If you change the variable, some of those flags disappear, even though the values don’t necessarily disappear:
use v5.10; use Devel::Peek; my $value = 137; $value = 'Buster'; Dump( $value );
Now the scalar’s value should be just Buster
, but the IV
slot still has the old 137
. However, the IOK
flag is gone:
SV = PVIV(0x100809208) at 0x100827840 REFCNT = 1 FLAGS = (PADMY,POK,pPOK) IV = 137 PV = 0x100202870 "Buster"\0 CUR = 6 LEN = 16
When you set the new value, and Perl hadn’t used it in a numeric context yet, there was no need to go through the work to translate the string value to the number value. Instead, it just unset the flag that denotes its okay to use the value as a number. You have to use it as a number to again:
use v5.10; use Devel::Peek; my $value = 137; $value = 'Buster'; say $value + 0; Dump( $value );
Now Perl converts it to a number and sets the numeric flags again:
0 SV = PVNV(0x100801e30) at 0x100827840 REFCNT = 1 FLAGS = (PADMY,POK,pIOK,pNOK,pPOK) IV = 0 NV = 0 PV = 0x100202870 "Buster"\0 CUR = 6 LEN = 16
That’s how it works if you go through the Perl interface, but if you play it a scalar through the XS interface, you can set whatever flags and values that you like. That’s exactly what Scalar::Util‘s dualvar
does that for you:
use v5.10; use Devel::Peek; use Scalar::Util qw(dualvar); my $value = dualvar 137, 'Buster'; Dump( $value ); say "$value"; say $value + 0;
Now you have a scalar which has unrelated numeric and string values, and the flags for both values are set:
SV = PVNV(0x100802010) at 0x1008277c8 REFCNT = 1 FLAGS = (PADMY,IOK,POK,pIOK,pPOK) IV = 137 NV = 0 PV = 0x100202870 "Buster"\0 CUR = 6 LEN = 16 Buster 137
Things to remember
- Scalars can have both numeric and string values at the same time
- Those two values can be unrelated
- You can create your own dualvar with Scalar::Util