Search code examples
phpyield

How to pass in an empty generator parameter?


I have a method which takes a generator plus some additional parameters and returns a new generator:

function merge(\Generator $carry, array $additional)
{
    foreach ( $carry as $item ) {
        yield $item;
    }
    foreach ( $additional as $item ) {
        yield $item;
    }
}

The usual use case for this function is similar to this:

function source()
{
    for ( $i = 0; $i < 3; $i++ ) {
        yield $i;
    }
}

foreach ( merge(source(), [4, 5]) as $item ) {
    var_dump($item);
}

But the problem is that sometimes I need to pass empty source to the merge method. Ideally I would like to be able to do something like this:

merge(\Generator::getEmpty(), [4, 5]);

Which is exactly how I would do in C# (there is a IEnumerable<T>.Empty property). But I don't see any kind of empty generator in the manual.

I've managed to work around this (for now) by using this function:

function sourceEmpty()
{
    if ( false ) {
        yield;
    }
}

And this works. The code:

foreach ( merge(sourceEmpty(), [4, 5]) as $item ) {
    var_dump($item);
}

correctly outputs:

int(4)
int(5)

But this is obviously not an ideal solution. What would be the proper way of passing an empty generator to the merge method?


Solution

  • I've found the solution:

    Since \Generator extends \Iterator I can just change the method signature to this:

    function merge(\Iterator $carry, array $additional) 
    {
        // ...
    

    This is input covariance thus it would break backward compatibility, but only if someone did extend the merge method. Any invocations will still work.

    Now I can invoke the method with PHP's native EmptyIterator:

    merge(new \EmptyIterator, [4, 5]);
    

    And the usual generator also works:

    merge(source(), [4, 5])