The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Sub::Go - DWIM sub blocks for smart matching

VERSION

version 0.01

SYNOPSIS

    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;
    };

DESCRIPTION

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.

Benefits

proper handling of hashes, with $a and $b for keys and values

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";
   };

context variables

Load $_ with the current value for arrays and scalars. Look for $a and $b for hash values.

in-place modification of original values

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/

prevent the block from running on undef values

We're tired of checking if defined is defined in loops.

    undef ~~ go { say "never runs" };
    undef ~~ sub { say "but we do" };

chaining of sub blocks

So you can bind several blocks, one after the other, in the opposite direction of map, grep and friends.

    $arr ~~ go { } go { } go { };

no warnings on the useless use of smart match operator in void context

Annoying warning for funky syntax overloading modules like this one or IO::All. Perl should have better way around this warning.

Pitfalls

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.

FEATURES

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.

    [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.

    [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
    };

return values

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.

next iterators

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.

    # DBIx::Class resultset
    
    $resultset->search({ age=>100 }) ~~ go {
        $_->name . " is centenary!";
    };

IMPORTS

go CODE

The main function here. Don't forget the semicolon at the end of the block.

yield VALUE

Iterate over into the next block in the chain.

    [qw/sue mike/] ~~ go { yield "world, $_" } go { say "hello " . shift };

skip

Tell the iterator to stop executing the current block and go to the next, if any.

    return skip;

stop

Tell the iterator to stop executing all blocks.

    return stop;

BUGS

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.

SEE ALSO

autobox::Core - has an each method that can be chained together

List::Gen

Sub::Chain