Sub::Go - DWIM sub blocks for smart matching
version 0.01
use Sub::Go; [ 1, 2, 3 ] ~~ go { say $_ }; # 1 # 2 # 3 # hashes with $a and $b %h ~~ go { say "key $a, value $b" }; undef ~~ go { # never gets called... }; '' ~~ go { # ...but this does }; # in-place modify my @rs = ( { name=>'jack', age=>20 }, { name=>'joe', age=>45 } ); @rs ~~ go { $_->{name} = 'sue' }; # filehandles open my $fh, '<', 'file.txt'; $fh ~~ go { my $line = shift; say ; # line by line }; # chaining @arr ~~ go { s/$/one/ } go { s/$/two/ }; # combine with signatures, or Method::Signatures # for improved sweetness use Method::Signatures; %h ~~ go func($x,$y) { say $x * $y; };
In case you don't know, smart matching (~~) data against a code block will run the block once (for scalars) or, distributively, many times for arrays and hashes:
~~
[1..10] ~~ sub { say shift }; @arr ~~ sub { say shift }; %h ~~ sub { ... };
The motivation behind this module is to improve the experience of using a code block with the smart match operator.
This module imports a sub called go into your package. This sub returns an object that overloads the smart match operator.
go
Smart matching sends only the keys, which may be useless if your hash is anonymous.
{ foo=>1, bar=>2 } ~~ go { say "key=$a, value=$b"; };
Load $_ with the current value for arrays and scalars. Look for $a and $b for hash values.
$_
$a
$b
But only in the first go block of a chain (although this may change soon).
my @arr = qw/a b c/; @arr ~~ go { s{$}{x} }; # now @arr is qw/ax bx cx/
We're tired of checking if defined is defined in loops.
undef ~~ go { say "never runs" }; undef ~~ sub { say "but we do" };
So you can bind several blocks, one after the other, in the opposite direction of map, grep and friends.
map
grep
$arr ~~ go { } go { } go { };
Annoying warning for funky syntax overloading modules like this one or IO::All. Perl should have better way around this warning.
A smart match (and most overloaded operators) can only return scalar values. So you can only expect to get a scalar (value or arrayref) from your block chaining.
You can chain go statements together, in the reverse direction as you would with map or grep.
say 10 ~~ go { return $_[0] * 2 } go { return $_[0] + 1 }; # 21
The next go block in the chain gets the return value from the previous block.
[1..3] ~~ go { say "uno " . $_[0]; 100 + $_[0] } go { say "due " . shift }; # uno 1 # uno 2 # uno 3 # due 101 # due 102 # due 103
To interleave two go blocks use the yield statement.
yield
[1..3] ~~ go { say "uno " . $_[0]; yield 100 + $_[0] } go { say "due " . shift }; # uno 1 # due 101 # uno 2 # due 102 # uno 3 # due 103
You can interrupt a go block with an special return statement: return skip.
return skip
[1..1000] ~~ go { # after 100 this block won't execute anymore return skip if $_[0] > 100; } go { # but this one will keep going up to the 1000th };
Or break the whole chain at a given point:
[1..1000] ~~ go { # after 100 this block won't execute anymore return stop if $_[0] > 100; } go { # this one will run 100 times too };
Scalar is the only return value from a smart match expression, and the same applies to go. You can only return scalars, no arrays and hashes. So we return an arrayref if your go chain returns more than one value.
# scalar my $value = 'hello' ~~ go { "$_[0] world" } # hello world # arrayref my $arr = [10..19] go { shift }; # @arr == 1, $arr[0] == 10
Just use map in this case, which is syntactically more sound anyway.
So, there's an alternative implementation for returning values, by chaining a reference to a variable, as such:
my @squares; @input ~~ go { $_ ** 2 } \@squares; my %hash = ( uno=>11, due=>22 ); my %out; %hash ~~ go { "xxx$_[0]" => $_[1] } \%out; # %out = ( xxxuno => 11, xxxdue => 22 )
Now you have a map like interface the other way around.
If you send the block an object which implements a method called next, the method will be automatically called and the return value fed to the block.
next
# DBIx::Class resultset $resultset->search({ age=>100 }) ~~ go { $_->name . " is centenary!"; };
The main function here. Don't forget the semicolon at the end of the block.
Iterate over into the next block in the chain.
[qw/sue mike/] ~~ go { yield "world, $_" } go { say "hello " . shift };
Tell the iterator to stop executing the current block and go to the next, if any.
return skip;
Tell the iterator to stop executing all blocks.
return stop;
This is pre-alfa, out in the CPAN for a test-drive. There are still inconsistencies in the syntax that need some more thought, so expect things to change badly.
PadWalker, a dependency, may segfault in perl 5.14.1.
autobox::Core - has an each method that can be chained together
each
List::Gen
Sub::Chain
To install Sub::Go, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Sub::Go
CPAN shell
perl -MCPAN -e shell install Sub::Go
For more information on module installation, please visit the detailed CPAN module installation guide.