Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show partition failure #147

Closed
wants to merge 3 commits into from
Closed

Conversation

AlexandruGG
Copy link
Collaborator

@AlexandruGG AlexandruGG commented Jul 14, 2021

I noticed an issue trying to use the new partition method from master. I am not sure exactly what's causing it, but I wrote a test to showcase -> whenever we run count on one of the results from the partition operation it causes this fatal error:

Fatal error: Generator passed to yield from was aborted without proper return and is unable to continue

In the other project where I was trying to use this I dumped the collection before-after the count:

Before

loophp\collection\Collection^ {#5180
  -parameters: array:1 [
    0 => Generator {#5177
      executing: {
        ./vendor/loophp/collection/src/Operation/Filter.php:46 { …}
      }
      closed: false
    }
  ]
  -source: Closure(iterable $iterable): Iterator^ {#5148
    returnType: "Iterator"
    class: "loophp\collection\Collection"
  }
}

After

loophp\collection\Collection^ {#5180
  -parameters: array:1 [
    0 => Generator {#5177
      closed: true
    }
  ]
  -source: Closure(iterable $iterable): Iterator^ {#5148
    returnType: "Iterator"
    class: "loophp\collection\Collection"
  }
}

Based on this it looks like the generator appears closed

@AlexandruGG
Copy link
Collaborator Author

@drupol hey, any clue what's going on here? 😅
have a look at the description and the tests I added

Comment on lines +64 to +71
public function it_fails_to_work_with_generator(): void
{
$gen = static fn (): Generator => yield from [1, 2, 3];

$this->beConstructedThrough('fromIterable', [$gen()]);
$this->shouldHaveCount(3);
$this->shouldIterateAs([1, 2, 3]);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the issue is here, when we instantiate the collection from a generator, which is what also happens in partition

@@ -670,7 +671,7 @@ public function partition(callable ...$callbacks): CollectionInterface
return new self(
Pipe::of()(
Partition::of()(...$callbacks),
Map::of()(static fn (Iterator $iterator): CollectionInterface => self::fromIterable($iterator))
Map::of()(static fn (Generator $generator): CollectionInterface => self::fromIterable(iterator_to_array($generator)))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this refactor it works looks like

@drupol
Copy link
Collaborator

drupol commented Jul 15, 2021

That's weird, this was supposed to work. I will most probably need to do git bisect in order to see where it comes from.

I made a small reproducer here:

<?php

/**
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

declare(strict_types=1);

namespace App;

use loophp\collection\Collection;

include __DIR__ . '/vendor/autoload.php';

$gen = static fn () => yield from range(1, 10);

$c = Collection::fromIterable($gen());

var_dump($c->all());
var_dump($c->all());
// or
// var_dump($c->count());
// var_dump($c->count());

@AlexandruGG
Copy link
Collaborator Author

That's weird, this was supposed to work. I will most probably need to do git bisect in order to see where it comes from.

I made a small reproducer here:

<?php

/**
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

declare(strict_types=1);

namespace App;

use loophp\collection\Collection;

include __DIR__ . '/vendor/autoload.php';

$gen = static fn () => yield from range(1, 10);

$c = Collection::fromIterable($gen());

var_dump($c->all());
var_dump($c->all());
// or
// var_dump($c->count());
// var_dump($c->count());

@drupol I found this: https://github.com/JeroenDeDauw/RewindableGenerator.

Can you tell what change we need to make in ClosureIterator to replicate the behaviour?

@drupol
Copy link
Collaborator

drupol commented Jul 15, 2021

@drupol I found this: https://github.com/JeroenDeDauw/RewindableGenerator.

Can you tell what change we need to make in ClosureIterator to replicate the behaviour?

I have doubts that using it would fix the issue.

The ClosureIterator is a "rewindable" iterator already.

@AlexandruGG
Copy link
Collaborator Author

@drupol I found this: https://github.com/JeroenDeDauw/RewindableGenerator.
Can you tell what change we need to make in ClosureIterator to replicate the behaviour?

I have doubts that using it would fix the issue.

The ClosureIterator is a "rewindable" iterator already.

You're right, the issue is not there but before that. Here's a more complete example:

<?php
declare(strict_types=1);

include __DIR__ . '/../../vendor/autoload.php';

use loophp\collection\Iterator\ClosureIterator;
use loophp\collection\Iterator\IterableIterator;


$gen = static fn () => yield from range(1, 10);

$c = new ClosureIterator($gen);

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // works

$c = new ClosureIterator(static fn (iterable $iterable): Iterator => new IterableIterator($iterable), $gen());

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // BOOM

$c = new IterableIterator($gen());

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // BOOM!

@AlexandruGG
Copy link
Collaborator Author

@drupol I found this: https://github.com/JeroenDeDauw/RewindableGenerator.
Can you tell what change we need to make in ClosureIterator to replicate the behaviour?

I have doubts that using it would fix the issue.
The ClosureIterator is a "rewindable" iterator already.

You're right, the issue is not there but before that. Here's a more complete example:

<?php
declare(strict_types=1);

include __DIR__ . '/../../vendor/autoload.php';

use loophp\collection\Iterator\ClosureIterator;
use loophp\collection\Iterator\IterableIterator;


$gen = static fn () => yield from range(1, 10);

$c = new ClosureIterator($gen);

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // works

$c = new ClosureIterator(static fn (iterable $iterable): Iterator => new IterableIterator($iterable), $gen());

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // BOOM

$c = new IterableIterator($gen());

var_dump(iterator_to_array($c)); // works
var_dump(iterator_to_array($c)); // BOOM!

The second and third cases are basically how Collection::fromIterable works

@drupol
Copy link
Collaborator

drupol commented Jul 15, 2021

This seems to fix the issue but creates some others:

diff --git a/src/Iterator/ClosureIterator.php b/src/Iterator/ClosureIterator.php
index 8af2e3c0..9d78331d 100644
--- a/src/Iterator/ClosureIterator.php
+++ b/src/Iterator/ClosureIterator.php
@@ -9,7 +9,7 @@ declare(strict_types=1);
 
 namespace loophp\collection\Iterator;
 
-use Generator;
+use Iterator;
 
 /**
  * @internal
@@ -48,10 +48,10 @@ final class ClosureIterator extends ProxyIterator
     }
 
     /**
-     * @return Generator<TKey, T>
+     * @return Iterator<TKey, T>
      */
-    private function getGenerator(): Generator
+    private function getGenerator(): Iterator
     {
-        return yield from ($this->callable)(...$this->arguments);
+        return ($this->callable)(...$this->arguments);
     }
 }

@drupol drupol added the bug Something isn't working label Jul 15, 2021
@drupol drupol mentioned this pull request Jul 15, 2021
3 tasks
@drupol
Copy link
Collaborator

drupol commented Jul 15, 2021

I guess #148 supersedes this?

@AlexandruGG
Copy link
Collaborator Author

I guess #148 supersedes this?

yeah I'll close this one once that gets merged

@drupol
Copy link
Collaborator

drupol commented Jul 15, 2021

I'll copy the tests over tomorrow.

@drupol drupol closed this in #148 Jul 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants