Perl 5’s list-flattening and reference-taking design choices
Perl has the strange property that its data structures try very hard to spill their contents all over the place. Despite having dedicated syntax for arrays –
@foois an array variable, distinct from the single scalar variable$foo– it’s actually impossible to nest arrays.my @foo = (1, 2, 3, 4); my @bar = (@foo, @foo); # @bar is now a flat list of eight items: 1, 2, 3, 4, 1, 2, 3, 4The idea, I guess, is that an array is not one thing. It’s not a container, which happens to hold multiple things; it is multiple things. Anywhere that expects a single value, such as an array element, cannot contain an array, because an array fundamentally is not a single value.
And so we have “references”, which are a form of indirection, but also have the nice property that they’re single values.
This is a common thing for people to find weird about Perl. Really though it’s just a different default.
Perl’s reference-taking operator is simply dual with the splat operator in Ruby and recent Javascript.
So, in Javascript, you can (as of some years ago) say this:
var x = [1, 2, 3, 4];
var y = [x, x]; // y => [[1, 2, 3, 4], [1, 2, 3, 4]]
var z = [...x, ...x]; // z => [1, 2, 3, 4, 1, 2, 3, 4]
That’s exactly like Perl, only the other way around. In Perl, splatting happens to containers by default when they’re mentioned in a list, and you have to use an operator to prevent it:
my @x = (1, 2, 3, 4);
my @y = (\@x, \@x); // @y => ([1, 2, 3, 4], [1, 2, 3, 4])
my @z = (@x, @x); // @z => (1, 2, 3, 4, 1, 2, 3, 4)
Note how if you always work with scalars containing references, rather than arrays, then the Perl form of this code becomes exactly equivalent to the Javascript:
my $x = [1, 2, 3, 4];
my $y = [$x, $x]; // $y => [[1, 2, 3, 4], [1, 2, 3, 4]]
my $z = [@$x, @$x]; // $z => [1, 2, 3, 4, 1, 2, 3, 4]
So the big difference is that Perl has lists as a first-class concept; within them it flattens containers, but it supplies you with an operator to prevent that. Other languages only have lists as a syntactic feature, limited to certain contexts such as list construction or argument passing; some of them supply an operator that allows you to flatten containers in such contexts, but many don’t.
That’s all.
Thank you, this was very helpful.
Some of this is historical, as Perl 4 didn't have references like we know them. More complicated structures like lists of lists required string gyrations to pack/unpack. Array references are so nice to have.
Thanks for that insight! Funny, I never made that connection, even though it’s so obvious now that you point it out. I knew that Perl 4 lacked nested data structures and that Perl 5 had to retrofit them into the language – but I never learned (or wrote any) Perl 4, I came up in Perl just after 5.5 came out, so I encountered list flattening and the
\operator as a single cohesive and seemingly intentional design. So I never made the leap to the fact that Perl 4’s pre-existing list-flattening and its omission of nested data structures was why Perl 5 approaches them in a different way from how most languages are designed.And I have to say these days I actively like it: flattening a list is a useful thing to be able to do wherever you need it. I don’t feel it needs to be the default (syntactically easiest) behavior, however languages where it’s not the default usually also don’t include any universal mechanism for it at all – which makes various bits of code more annoying to write.