I love function signatures in Perl, and you should too.
Tags: perl, codequality
Published: 2020-10-12 19:08:40

One of the most common tasks in programming is receiving arguments within a function. In Perl, historically, this task has taken on a number of forms, and it has usually looked like this:

```perl
sub foo {
  my $arg1 = shift;
  my $arg2 = shift;

  return $arg1 + $arg2;
}
```

As many Perlers already know, the `shift` statement is just short-hand for `shift @_`, which takes the first item off of the special `@_` list.

There have been other variations on this theme:

```perl
# Named arguments
sub foo {
  my %args = @_;
  my $arg1 = delete $args{arg1};
}

# Named arguments with a hashref
sub foo {
  my ($args) = @_;
  my $arg1 = delete $args->{arg1};
}

# Positional arguments in a single assignment
sub foo {
  my ($arg1, $arg2) = @_;
}
```

This is a marvelous amount of flexibility, and many folks have found incredible ways of making this work to their advantage. It is a very different way of doing function arguments, though, and if you've been doing Perl long enough you've already spotted that all of the samples above will have that most beloved of warnings, `Use of uninitialized value`.

Of course, you could use a default:

```perl
sub foo {
  my $foo = shift || 1;
}
```

but what if you _want_ your caller to get an error if they fail to give you enough arguments? Well, you could use function prototypes, like this:

```perl
sub foo($) {
  my $foo = shift;
}
```

If you did it this way, code calling this function as `foo()` would get the `Not enough arguments for main::foo` error. You then would still be able to handle arguments the same way as you normally would after using the prototype, though.

However, the Perl community has long discouraged the use of prototypes, as the syntax is a little hairy and it's not always clear what the developer was _trying_ to do when they started using them.

Fast-forward to Perl 5.20 and now you have signatures!

Quick note: [Brian D Foy did a much better write-up here](https://www.effectiveperlprogramming.com/2015/04/use-v5-20-subroutine-signatures/) than I'm going to. I'm only going to tell you why I love it.

With function signatures, I can remove boilerplate (many will know that this is one of the time-honored traditions of Perl programming, dispensing with boilerplate) and instead I can focus on the intention of my code.

```perl
sub some_function($a, $b) {
  return $a + $b;
}
```

I can even do this with closures:

```perl
my $some_closure = sub ($a, $b) {
  return $a + $b;
}
```

Right; I have a pretty syntax that I like, whoop-dee-doo for me, right? Well, here's why you like it.

Remember that Mojolicious thing that I've ranted about before? That you should be loving right now? Or any of those event-based modules that you've been loving? This is where the rubber and road fall in love and get a puppy together.

```perl
# This is pseudo-code that might run!
use Mojo::Promise;

sub long_running_operation() {
  state $cached = [];

  return (scalar @{$cached} > 0)
    ? Mojo::Promise->resolve( shift @{$cached} )
    : Mojo::Promise->new( sub ($resolve, $reject) {
      my $retval = eval {
        perform_slow_lookup();
      }; if ($@ or not $retval) {
        return $reject->($@);
      }

      return $resolve->($retval);
    })->then( sub ($results = []) {
      $cached = { map { $_->{name} => $_ } @{$results} };
      return Mojo::Promise->resolve( shift @{$cached}; );
    });
}
```

Oh, did I say I was going to make you fall in love with `Mojo::Promise`, too? My bad, I should have warned you.

So now, with function signatures you have eliminated a remarkable amount of boilerplate here (about four lines in just this snippet), which reduces your surface area for bugs while also making your code smaller and more expressive.

Without signatures, this code would look something like this:

```perl
# This is pseudo-code that might run!
use Mojo::Promise;

sub long_running_operation() {
  state $cached = [];

  return (scalar @{$cached} > 0)
    ? Mojo::Promise->resolve( shift @{$cached} )
    : Mojo::Promise->new( sub {
      my ($resolve, $reject) = @_;
      my $retval = eval {
        perform_slow_lookup();
      }; if ($@ or not $retval) {
        return $reject->($@);
      }

      return $resolve->($retval);
    })->then( sub {
      my $results = shift || [];
      $cached = { map { $_->{name} => $_ } @{$results} };
      return Mojo::Promise->resolve( shift @{$cached}; );
    });
}
```

See? It's not the end of the world, but it does take away some of the clarity in the code by adding boilerplate lines that you have to visually parse through in order to see what you're trying to do.

Anyway, that's just what crossed my mind while I'm writing more things to trawl GitHub.

Have a great day!