Create your own dualvars

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