Use the return value of given

Perl 5.14, when it’s released, allows you to use a return value from given-when. You have to wrap it in a do (Item 25: Use do {} to create inline subroutines):


use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits' }
	when( /^\p{L}+\z/ ) { 'alphabetics' }
	when( /^\p{P}+\z/ ) { 'punctuation' }
	default             { 'something else' }
	}
	};

say $value;

In this example, you use the \p{...} to match Unicode properties (Item 76: Match Unicode characters and properties). Here are a couple of sample runs, using Perl 13.5, the development version leading to Perl 5.14 (Item 110: Compile and install your own perl):

$ perl5.13.5 do-given.pl 123
digits
$ perl5.13.5 do-given.pl abc
alphabetics
$ perl5.13.5 do-given.pl .,; 
punctuation
$ perl5.13.5 do-given.pl "|" 
something else

This has the same advantages as other uses of do where you can avoid multiple, parallel assignments to the same variable. Prior to Perl 5.14, you would have to do each assignment in each branch, which is quite tedious:

use 5.010;

my $value;
given ( $ARGV[0] ) { # lots of repeated $var =
	when( /^\p{N}+\z/ ) { $var = 'digits' }
	when( /^\p{L}+\z/ ) { $var = 'alphabetics' }
	when( /^\p{P}+\z/ ) { $var = 'punctuation' }
	default             { $var = 'something else' }
	}
	};

say $value;

As with other subroutines and subroutine-like things, the result of the given is the last evaluated expression. That’s not the same thing as the last expression, or even the last when block. Remember that you can have interstitial statements too.

Consider the case with some statements after the default block:

use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits' }
	say 'Not digits';
	when( /^\p{L}+\z/ ) { 'alphabetics' }
	say 'Not alphabetics';
	when( /^\p{P}+\z/ ) { 'punctuation' }
	say 'Not punctuation';
	default             { 'something else' }
	'last';
	}
	};

say $value;

No matter what you do, the given will never return last because either a when or the default block runs.

$ perl5.13.5 do-given.pl "|" 
Not digits
Not alphabetics
Not punctuation
something else

All blocks have an implicit break at the end unless you say otherwise:

use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits'; break }
	say 'Not digits';
	when( /^\p{L}+\z/ ) { 'alphabetics'; break }
	say 'Not alphabetics';
	when( /^\p{P}+\z/ ) { 'punctuation'; break }
	say 'Not punctuation';
	default             { 'something else'; break }
	'last';
	}
	};

say $value;

Here’s another case. Without the default block, you have no guarantee that at least one block will run so you might evaluate that final statement:

use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits' }
	when( /^\p{L}+\z/ ) { 'alphabetics' }
	when( /^\p{P}+\z/ ) { 'punctuation' }
	'last';
	}
	};

say $value;

Now the fall-through cases evaluate that final last string which becomes the return value:

$ perl5.13.5 do-given.pl "|" 
last

There’s another case to consider though. What it there is no default block and there are no additional statements after the last when? What’s the last evaluated expression then? Here’s a when condition where the value is always false:

use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits' }
	when( /^\p{L}+\z/ ) { 'alphabetics' }
	when( /^\p{P}+\z/ ) { 'punctuation' }
	when( 1 == 3 )      { 'always_false' }
	}
	};

say "value is defined" if defined $value;
say "value is [$value]";

In this case, the return value is both false and undefined, which is just fine:

$ perl5.13.5 do-given.pl    
value is []

Check out this case, where the last evaluated expression is a true value in the when condition, but there’s nothing explicit in the block:

use 5.013;

my $value = do { given ( $ARGV[0] ) {
	when( /^\p{N}+\z/ ) { 'digits' }
	when( /^\p{L}+\z/ ) { 'alphabetics' }
	when( /^\p{P}+\z/ ) { 'punctuation' }
	when( 1 == 1 )      { }
	'last'
	}
	};

say "value is defined" if defined $value;
say "value is [$value]";

With no argument, your script should trigger the 1 == 1 condition, which is always true (and also the same as a default). The return value is undefined here too, even though the last evaluated, explicit expression is true:

$ perl5.13.4 do-given.pl  
value is []

Not that you should ever have an empty block, but it’s an interesting edge case.

Things to remember

  • In Perl 5.14, given returns the last evaluated expression but you have to wrap it in a do to use it.
  • The last evaluated expression is not necessarily the last expression.
  • To try these new features, you need to use the experimental track until that stable version is released.

4 thoughts on “Use the return value of given”

  1. Why is the “do” necessary? I know it’s probably because perl hasn’t been changed to not need it, but why did they think it was necessary to make people use the “do”? Just seems like extra syntax too me.

  2. I thought the extra do was odd too, but I haven’t bothered to find out why yet. It’s really hard to search perl5-porters for messages with “do” or “given” in them. :)

  3. what about similar constructs, like foreach / for+when / if+elsif+else, will they start magically returning values? if not, why not?

  4. The foreach has a return value if you spell it map, and you can easily do the same thing with if and a do block. Perl 6 has a gather which might do some of this more naturally.

Comments are closed.