Goodbye fake multidimensional data structures

This is a chapter in Perl New Features, a book from Perl School that you can buy on LeanPub or Amazon. Your support helps me to produce more content.


Perl v5.36 disables by default a Perl 3 feature that you may have never seen, which I’ll cover in the next section. In a single-element access to a hash, you were able to use a list of indices to simulate a multidimensional data structure. This was long before Perl had references (and shortly before Taylor Swift was born). Yet, in v5.34, this still works:

use v5.34;
my %hash;
$hash{1,3} = 7;  # modern way is $array[1][3] = 7;

In v5.36, this still works as long as you specify a minimum Perl version with require instead of use:

require v5.36;
my %hash;
$hash{1,3} = 7;

With use, though, Perl loads the appropriate feature bundle, which turns off this feature:

use v5.36;
my %hash;
$hash{1,3} = 7;

The output shows that the multidimensional feature was removed from the list:

Multidimensional hash lookup is disabled at ...
Execution of ... aborted due to compilation errors.

This version also disables switch and indirect.

Enable multidimensional explicitly if you want to restore it, although you should only do this for legacy code since the feature is likely disappear completely:

use v5.36;
use warnings;
use feature qw(multidimensional);

Perl 4’s fake multidimensional hashes

Perl 4 did not have references, so it didn’t have a way include an array in an array. However, you could fake it with a special hash
key convention that Perl stole from Awk.

To make this work, you access a single element in the hash, but have a “list” of comma-separated index values as the hash key:

my %hash;
$hash{0,0} = "a";
$hash{1,1} = "b";

for( my $row = 0; $row <= 1; $row++ ) {
	for( my $col = 0; $col <= 1; $col++ ) {
		printf '% 2s', $hash{$row,$col} // 0;
		}
	print "\n";
	}

This outputs your tiny matrix:

 a 0
 0 b

To see what Perl is doing, dump that hash. The $Useqq variable from Data::Dumper encodes unprintables as their ordinal values in octal"

$Data::Dumper::Useqq = 1;
say Dumper(\%hash);
$VAR1 = {
		  "0\0340" => "a",
		  "1\0341" => "b"
		};

That \034 is (U+001C INFORMATION SEPARATOR FOUR):

printf '% 2s', $hash{ "$row\034$col" } // 0;

Perl calls that $; (or $SUBSCRIPT_SEPARATOR or $SUBSEP). Larry had wanted to use $, but that was already the output field separator. You can use this value directly:

printf '% 2s', $hash{ "$row$;$col" } // 0;

You can mix the forms too. This examples assigns with the comma notation but outputs it by constructing the key itself:

$row = 1;
$col = 3;

$hash{$row,$col} = 7;

printf '% 2s', $hash{ "$row$;$col" } // 0;

Change the separator

If \034 is something that might appear in your data, change the multidimensional separator to something that won't conflict:

local $; = '\000'

From the Perl documentation