I implemented the bind function in my Perl Seq Module. bind is a very useful function. For example here is flatten that I implemented with it.

flatten

Flatten takes a sequence of sequences and returns a single sequence.

1
2
3
4
5
6
my $flattened =
    Seq->wrap(
        Seq->wrap(1,1),
        Seq->wrap(2,3,5,8,13),
    )
    ->flatten;

This is how a non-lazy Perl implemenation would look like:

1
2
3
4
5
6
7
8
9
sub flatten($aoa) {
    my @flattened;
    for my $outer ( @$aoa ) {
        for my $inner ( @$outer ) {
            push @flattened, $inner;
        }
    }
    return \@flattened;
};

Using it looks very similar.

1
2
3
4
5
my $flattened =
    flatten([
        [1,1],
        [2,3,5,8,13],
    ]);

This is how flatten is implemented in Seq.

1
2
3
4
# flatten : Seq<Seq<'a>> -> Seq<'a>
sub flatten($iter) {
    return bind($iter, $id);
}

$id is the id-function. It’s implementation:

1
my $id = sub($x) { return $x }

I also could write.

1
2
3
sub flatten($iter) {
    bind($iter, sub($x) { $x });
}

bind is basically binding a value from left to right. Its like assignment. It binds one value from the $iter sequence to the value $x.

The function we pass to, is executed for every value. But only when needed.

We could say that bind is like a lazy for-loop.

1
2
3
for my $x ( @$iter ) {
    ...
}

consider for as a function. It binds one value of @$iter to $x and executes the function we pass with { ... }. But a for-loop does it in a non-lazy way. It computes all values at once. Even if you only want to acces the first value of it.

Seq->flatten instead creates a new sequence without doing anything as long you never ask it to evaluate.

Inside the lambda we passed to flatten we must return another sequence. But bind will flatten the results to a single sequence.

This is why $id is passed. It returns the inner sequence as-is.

bind itself could be implemented with map and flatten.

1
2
3
sub bind($seq, $f) {
    return $seq->map($f)->flatten;
}