Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- `Innmind\BlackBox\Set::zip()`
- `Innmind\BlackBox\Set::properties()`
- `Innmind\BlackBox\Set::slice()`
- `Innmind\BlackBox\Set\Seed::toSet()`
- `Innmind\BlackBox\Set\Provider\Strings::mutuallyExclusive()`
- `Innmind\BlackBox\Runner\Proof::tagged()`
- `Innmind\BlackBox\Runner\Proof::disableShrinking()`
Expand Down Expand Up @@ -88,6 +89,9 @@
- `Innmind\BlackBox\Set\MutuallyExclusive`
- `Innmind\BlackBox\Set\UnsafeStrings::any()`
- `Innmind\BlackBox\Set\Uuid`
- `Innmind\BlackBox\Set\Seed::map()`
- `Innmind\BlackBox\Set\Seed::flatMap()`
- `Innmind\BlackBox\Set\Seed::shrink()`
- `Innmind\BlackBox\Set::decorate()`
- `Innmind\BlackBox\Set::call()`
- `Innmind\BlackBox\Set::generator()`
Expand Down
10 changes: 4 additions & 6 deletions documentation/sets.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,16 @@ As you can see with `flatMap` you can locally define what you want without havin
};

$set = Set::integers()->flatMap(
static fn(Seed $int) => Set::strings()->map(
static fn(string $string) => $int->map(
static fn(int $int) => $int.$string,
),
static fn(Seed $int) => Set::compose(
static fn(string $string, int $int) => $int.$string,
Set::strings(),
$int->toSet(),
),
);
```

This way BlackBox knows every transformations of a seeded value and re-apply then after shrinking it.

And you can also compose multiple `Seed`s via the `Seed::flatMap()` method.

??? warning "Randomness"
By default the `Set` returned by `flatMap` will produce values with the same _seed_ (the callable argument).

Expand Down
82 changes: 48 additions & 34 deletions proofs/set.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Set,
Random,
Tag,
Exception\EmptySet,
};

return static function($prove) {
Expand Down Expand Up @@ -147,10 +148,10 @@
'Set::flatMap() input value is shrinkable',
static function($assert) {
$compose = Set::integers()->flatMap(
static fn($seed) => Set::strings()->map(
static fn($string) => $seed->map(
static fn($i) => $i.$string,
),
static fn($seed) => Set::compose(
static fn($string, $i) => $i.$string,
Set::strings(),
$seed->toSet(),
),
);

Expand All @@ -170,10 +171,9 @@ static function($assert) {

$compose = Set::strings()->flatMap(
static fn($seed) => Set::compose(
static fn($a, $b) => $seed->map(
static fn($string) => $a.$string.$b,
),
static fn($a, $string, $b) => $a.$string.$b,
Set::integers(),
$seed->toSet(),
Set::integers(),
),
);
Expand All @@ -198,15 +198,13 @@ static function($assert) {
static function($assert) {
$compose = Set::strings()->flatMap(
static fn($stringSeed) => Set::integers()->flatMap(
static fn($aSeed) => Set::integers()->map(
static fn($b) => $stringSeed
->flatMap(
static fn($string) => $aSeed->map(
static fn($a) => $a.'|'.$string.'|'.$b,
),
)
->map(static fn($string) => "($string)"),
),
static fn($aSeed) => Set::compose(
static fn($a, $string, $b) => $a.'|'.$string.'|'.$b,
$aSeed->toSet(),
$stringSeed->toSet(),
Set::integers(),
)
->map(static fn($string) => "($string)"),
),
);

Expand Down Expand Up @@ -234,12 +232,10 @@ static function($assert) {
$compose = Set::strings()->flatMap(
static fn($stringSeed) => Set::integers()->flatMap(
static fn($aSeed) => Set::compose(
static fn($b, $stringB) => $stringSeed->flatMap(
static fn($string) => $aSeed->map(
static fn($a) => $a.'|'.$string.'|'.$stringB.'|'.$b,
),
),
static fn($a, $b, $string, $stringB) => $a.'|'.$string.'|'.$stringB.'|'.$b,
$aSeed->toSet(),
Set::integers(),
$stringSeed->toSet(),
Set::strings(),
),
),
Expand Down Expand Up @@ -267,10 +263,11 @@ static function($assert) {
'Set::flatMap()->map()->filter()',
static function($assert) {
$compose = Set::integers()->flatMap(
static fn($seed) => Set::strings()
->map(static fn($string) => $seed->map(
static fn($i) => $i.$string,
))
static fn($seed) => Set::compose(
static fn($string, $i) => $i.$string,
Set::strings(),
$seed->toSet(),
)
->filter(static fn($string) => $string !== '0'),
);

Expand Down Expand Up @@ -299,12 +296,12 @@ static function($assert) {
static function($assert) {
$compose = Set::integers()->flatMap(
static fn($seedA) => Set::integers()->flatMap(
static fn($seedB) => Set::strings()
->map(static fn($string) => $seedA->flatMap(
static fn($a) => $seedB->map(
static fn($b) => $a.$string.$b,
),
))
static fn($seedB) => Set::compose(
static fn($a, $string, $b) => $a.$string.$b,
$seedA->toSet(),
Set::strings(),
$seedB->toSet(),
)
->filter(static fn($string) => $string !== '00'),
),
);
Expand Down Expand Up @@ -337,10 +334,9 @@ static function($assert) {
static function($assert) {
$compose = Set::strings()->madeOf(Set::of('a'))->flatMap(
static fn($seed) => Set::compose(
static fn($a, $b) => $seed->map(
static fn($string) => $a.'|'.$string.'|'.$b,
),
static fn($a, $string, $b) => $a.'|'.$string.'|'.$b,
Set::integers()->above(0), // to simplify the assertion
$seed->toSet(),
Set::integers()->above(0),
)->filter(static fn($string) => $string !== '0||0'),
);
Expand Down Expand Up @@ -474,4 +470,22 @@ static function($assert) {
},
)
->tag(Tag::ci, Tag::local);

yield $prove
->proof('Filtered out seed throws EmptySet')
->given($anySet)
->test(static function($assert, $set) {
$assert->throws(
static fn() => $set
->flatMap(
static fn($seed) => $seed
->toSet()
->filter(static fn() => false),
)
->enumerate()
->current(),
EmptySet::class,
);
})
->tag(Tag::ci, Tag::local, Tag::wip);
};
5 changes: 3 additions & 2 deletions src/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public static function realNumbers(): Provider\RealNumbers
* @template A
* @no-named-arguments
*
* @param callable(mixed...): (A|Seed<A>) $aggregate It must be a pure function (no randomness, no side effects)
* @param callable(mixed...): A $aggregate It must be a pure function (no randomness, no side effects)
*
* @return self<A>
*/
Expand Down Expand Up @@ -431,7 +431,7 @@ public function exclude(callable $predicate): self
*
* @template V
*
* @param callable(T): (V|Seed<V>) $map
* @param callable(T): V $map
*
* @return self<V>
*/
Expand Down Expand Up @@ -467,6 +467,7 @@ public function flatMap(callable $map): self
Set\FlatMap::implementation(
static fn($input) => $map($input)->toSet()->implementation,
$this->implementation,
self::build(...),
),
$this->disableShrinking,
);
Expand Down
23 changes: 20 additions & 3 deletions src/Set/FlatMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,38 @@ final class FlatMap implements Implementation
*
* @param \Closure(Seed<I>): Implementation<D> $decorate
* @param Implementation<I> $set
* @param \Closure(Implementation<I>): Set<I> $wrap
*/
private function __construct(
private \Closure $decorate,
private Implementation $set,
private \Closure $wrap,
) {
}

#[\Override]
public function __invoke(Random $random, \Closure $predicate): \Generator
{
$yielded = false;

// By default we favor reusing the same seed to generate multiple values
// from the underlying set. To generate a more wide range of seeds one
// can use the ->randomize() method.
foreach (($this->set)($random, static fn() => true) as $seed) {
$set = ($this->decorate)(Seed::of($seed));
$set = ($this->decorate)(Seed::of(
$this->wrap,
$seed,
));

foreach ($set($random, $predicate) as $value) {
yield $value;
$yielded = true;
}

yield from $set($random, $predicate);
// This means the underlying Set cannot produce any value
if (!$yielded) {
return;
}
}
}

Expand All @@ -50,13 +65,15 @@ public function __invoke(Random $random, \Closure $predicate): \Generator
*
* @param callable(Seed<V>): Implementation<T> $decorate It must be a pure function (no randomness, no side effects)
* @param Implementation<V> $set
* @param \Closure(Implementation<V>): Set<V> $wrap
*
* @return self<T,V>
*/
public static function implementation(
callable $decorate,
Implementation $set,
\Closure $wrap,
): self {
return new self(\Closure::fromCallable($decorate), $set);
return new self(\Closure::fromCallable($decorate), $set, $wrap);
}
}
14 changes: 3 additions & 11 deletions src/Set/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,9 @@ public function __invoke(
\Closure $predicate,
): \Generator {
$map = $this->map;
$mappedPredicate = static function(mixed $value) use ($map, $predicate): bool {
$mappedPredicate = static fn(mixed $value): bool =>
/** @var I $value */
$mapped = $map($value);

if ($mapped instanceof Seed) {
/** @var D */
$mapped = $mapped->unwrap();
}

return $predicate($mapped);
};
$predicate($map($value));

foreach (($this->set)($random, $mappedPredicate) as $value) {
yield Value::of($value)
Expand All @@ -59,7 +51,7 @@ public function __invoke(
* @template T
* @template V
*
* @param callable(V): (Seed<T>|T) $map It must be a pure function (no randomness, no side effects)
* @param callable(V): T $map It must be a pure function (no randomness, no side effects)
* @param Implementation<V> $set
*
* @return self<T,V>
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Integers.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(int): (V|Seed<V>) $map
* @param callable(int): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Properties.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(Ensure): (V|Seed<V>) $map
* @param callable(Ensure): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/RealNumbers.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(float): (V|Seed<V>) $map
* @param callable(float): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Sequence.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public function exclude(callable $predicate): Set
*
* @template U
*
* @param callable(list<V>): (U|Seed<U>) $map
* @param callable(list<V>): U $map
*
* @return Set<U>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Slice.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(Util): (V|Seed<V>) $map
* @param callable(Util): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(string): (V|Seed<V>) $map
* @param callable(string): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Strings/Chars.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(non-empty-string): (V|Seed<V>) $map
* @param callable(non-empty-string): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Strings/MadeOf.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(string): (V|Seed<V>) $map
* @param callable(string): V $map
*
* @return Set<V>
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Set/Provider/Strings/Unicode.php
Original file line number Diff line number Diff line change
Expand Up @@ -3191,7 +3191,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
* @param callable(string): (V|Seed<V>) $map
* @param callable(string): V $map
*
* @return Set<V>
*/
Expand Down
Loading
Loading