5 Ways You Can Embrace the Magic of Perl
Tags: perl, programming
Published: 2021-02-05 20:19:11

Perl is said (sometimes frustratingly) to be a do-what-I-mean programming language.  Many of its statements and constructions are [designed to be forgiving](https://perldoc.pl/perlintro#Safety-net) or have [analogies to natural languages](http://wall.org/~larry/natural.html). Still others are said to be "magic," behaving differently depending on how they're used. Adept use of Perl asks you to not only *understand* this magic, but to *embrace* it and the expressiveness it enables. Here, then, are five ways you can bring some magic to your code.

## [`$_`](https://perldoc.pl/perlvar#$_)

Perl has many [special variables](https://perldoc.pl/perlvar#SPECIAL-VARIABLES), and first among them (literally, it's the first documented) is [`$_`](https://perldoc.pl/perlvar#$_). Also spelled `$ARG` if you `use` the [`English`](https://perldoc.perl.org/English) module, the documentation describes it as "the default input and pattern-matching space." Many, many functions and statements will assume it as **the default or implicit argument**; you can find the full list in the [documentation](https://perldoc.pl/perlvar#$_). Here's an example that uses it implicitly to output the numbers from 1 to 5:

```perl
say for 1 .. 5;
```
Output:
```
1
2
3
4
5
```

Where some languages require an iterator variable in a `for` or `foreach` loop, in the absence of one Perl assigns it to `$_`.

## Statement modifiers

We then use our second trick; where some other languages require a block to enclose every loop or conditional (whether denoted by braces `{` `}` or indentation), Perl allows you to put said looping or conditional statement [after a single other statement](https://perldoc.pl/perlsyn#Statement-Modifiers), in this case the [`say`](https://perldoc.perl.org/functions/say) which prints its argument(s) followed by a newline.

However, above we have *no* arguments passed to `say` and so once again the default `$_` is used, now containing a number from 1 to 10 which is then printed out. It's a very powerful and expressive idiom, enabling both the writer and reader of code to concentrate on the important thing that's happening. **It's also entirely optional.** You can just as easily type:

```perl
for my $foo (1..5) {
    say $foo;
}
```

But where's the magic in that?

## Magic variables and [`use English`](https://perldoc.pl/English)

We mentioned the `$_` variable above, and that it could also be spelled `$ARG` if you add `use English` to your code. It can be hard to read code with large amounts of punctuation, though, and even harder to remember what each variable does. Thankfully the [`English`](https://perldoc.pl/English) module provides aliases, and the [perlvar](https://perldoc.pl/perlvar#SPECIAL-VARIABLES) man page lists them in order. It's much easier to read and write things like [`$LIST_SEPARATOR`](https://perldoc.pl/perlvar#$LIST_SEPARATOR), [`$PROCESS_ID`](https://perldoc.pl/perlvar#$PROCESS_ID), or [`$MATCH`](https://perldoc.pl/perlvar#$MATCH) rather than `$"`, `$$`, and `$&`, and goes a long way towards reducing Perl's reputation as a write-only language.

## [List and scalar contexts](https://perldoc.pl/perldata#Context)

Like natural languages, Perl has a concept of "context" in which words mean different things depending on their surroundings. In Perl's case, expressions may behave differently depending on whether they expect to produce a list of values or a single value, called a [*scalar*](https://perldoc.perl.org/perldata#Scalar-values). Here's a trivial example:

```perl
my @foo = (1, 2, 3); # list context, @foo contains the list
my $bar = (1, 2, 3); # scalar context, $bar contains 3
```

In the first line, we assign the list of numbers `(1, 2, 3)` to the array `@foo`. But in the second line, we're assigning to the scalar variable `$bar`, which now contains the last item in the list.

Here's another example, using the [`reverse`](https://perldoc.perl.org/functions/reverse) function:

```perl
my @foo = ('one', 'two', 'three');
my @bar = reverse @foo; # @bar contains ('three', 'two', 'one')
my $baz = reverse @foo; # $baz contains 'eerhtowteno'
```

In list context, `reverse` takes its arguments and returns them in the opposite order. But in scalar context, it *concatenates* all of the arguments together and returns a string with the *characters* in opposite order.

In general, "there is no general rule for deducing a function's behavior in scalar context from its behavior in list context." *[(Dominus 1998)](https://perl.plover.com/context.html)* You'll just have to [look up the function](https://perldoc.perl.org/perlfunc) to determine what it does, though in general, it does what you want, but if you want to force scalar context use the [`scalar`](https://perldoc.perl.org/perlfunc) operator:

```perl
my @foo = ('aa', 'aab', 'bbc');
my @bar = scalar grep /aa/, @foo; # returns a list (2), counting the number of matches
```

## Hash [slices](https://perldoc.pl/perldata#Slices)

One of Perl's three built-in data types is the hash, also known as an associative array. It's an unordered collection of scalars indexed by string, rather than the numbers used by normal arrays. It's a useful construct, and you can develop [complicated data structures](https://perldoc.pl/perldsc) using just scalars, arrays, and hashes. What's not widely known is that you can access several elements of of a hash using a hash slice, using syntax that's similar to array slices. Here's an example:

```perl
my ($who, $home) = @ENV{'USER', 'HOME'};
```

It works the other way, too: you can assign to a slice.

```perl
@colors{'red', 'green', 'blue'} = (0xff0000, 0x00ff00, 0x0000ff);
```

I use this a lot when assigning arguments received from functions or methods (see [my previous article on subroutine signatures](https://phoenixtrap.com/index.php/2021/01/27/better-perl-with-subroutine-signatures-and-type-validation)):

```perl
use v5.24; # for postfix dereferencing
use Types::Standard qw(Str Int);
use Type::Params 'compile_named';

foo('hello', 42);

sub foo {
    state $check = compile_named(
        param1 => Str,
        param2 => Int, {optional => 1},
    );
    my ($param1, $param2) =
        $check->(@_)->@{'param1', 'param2'};

    say $param1, $param2;
}
```

In the example above, `$check->(@_)` returns the type-checked arguments to the `foo()` function courtesy of [Type::Params](https://metacpan.org/pod/Type::Params)' `compile_named()` function. It's returned as a hash reference, and since hashes are unordered, we specify the order in which we want the values by dereferencing and then slicing the resulting hash. The [postfix dereferencing syntax](https://perldoc.pl/perlref#Postfix-Reference-Slicing) was added in Perl 5.20 and made a default feature in 5.24, and reduces the number of nested brackets and braces we have to deal with.

## Conclusion

I hope this article has given you a taste of some of the magic available in the Perl language. It's these sort of features that make programming in it a bit more joyful. As always, [check the documentation](https://perldoc.pl/) for complete information on these and other topics, or look for answers and ask questions on [PerlMonks](https://www.perlmonks.org/) or [Stack Overflow](https://stackoverflow.com/questions/tagged/perl).