diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f51d9b7..252e388f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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()`
@@ -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()`
diff --git a/documentation/sets.md b/documentation/sets.md
index 131315e5..722a4a57 100644
--- a/documentation/sets.md
+++ b/documentation/sets.md
@@ -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).
diff --git a/proofs/set.php b/proofs/set.php
index 5fb106d9..2bc9d370 100644
--- a/proofs/set.php
+++ b/proofs/set.php
@@ -5,6 +5,7 @@
Set,
Random,
Tag,
+ Exception\EmptySet,
};
return static function($prove) {
@@ -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(),
),
);
@@ -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(),
),
);
@@ -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)"),
),
);
@@ -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(),
),
),
@@ -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'),
);
@@ -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'),
),
);
@@ -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'),
);
@@ -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);
};
diff --git a/src/Set.php b/src/Set.php
index b76d4351..42f78aba 100644
--- a/src/Set.php
+++ b/src/Set.php
@@ -77,7 +77,7 @@ public static function realNumbers(): Provider\RealNumbers
* @template A
* @no-named-arguments
*
- * @param callable(mixed...): (A|Seed) $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
*/
@@ -431,7 +431,7 @@ public function exclude(callable $predicate): self
*
* @template V
*
- * @param callable(T): (V|Seed) $map
+ * @param callable(T): V $map
*
* @return self
*/
@@ -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,
);
diff --git a/src/Set/FlatMap.php b/src/Set/FlatMap.php
index b17d29f1..1e88c796 100644
--- a/src/Set/FlatMap.php
+++ b/src/Set/FlatMap.php
@@ -21,23 +21,38 @@ final class FlatMap implements Implementation
*
* @param \Closure(Seed): Implementation $decorate
* @param Implementation $set
+ * @param \Closure(Implementation): Set $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;
+ }
}
}
@@ -50,13 +65,15 @@ public function __invoke(Random $random, \Closure $predicate): \Generator
*
* @param callable(Seed): Implementation $decorate It must be a pure function (no randomness, no side effects)
* @param Implementation $set
+ * @param \Closure(Implementation): Set $wrap
*
* @return self
*/
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);
}
}
diff --git a/src/Set/Map.php b/src/Set/Map.php
index 347ed1d0..f7ca87f6 100644
--- a/src/Set/Map.php
+++ b/src/Set/Map.php
@@ -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)
@@ -59,7 +51,7 @@ public function __invoke(
* @template T
* @template V
*
- * @param callable(V): (Seed|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 $set
*
* @return self
diff --git a/src/Set/Provider/Integers.php b/src/Set/Provider/Integers.php
index 2ab7d64c..d22c502b 100644
--- a/src/Set/Provider/Integers.php
+++ b/src/Set/Provider/Integers.php
@@ -151,7 +151,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(int): (V|Seed) $map
+ * @param callable(int): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Properties.php b/src/Set/Provider/Properties.php
index 28d8c36d..200c220e 100644
--- a/src/Set/Provider/Properties.php
+++ b/src/Set/Provider/Properties.php
@@ -102,7 +102,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(Ensure): (V|Seed) $map
+ * @param callable(Ensure): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/RealNumbers.php b/src/Set/Provider/RealNumbers.php
index 41382153..f179aacf 100644
--- a/src/Set/Provider/RealNumbers.php
+++ b/src/Set/Provider/RealNumbers.php
@@ -110,7 +110,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(float): (V|Seed) $map
+ * @param callable(float): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Sequence.php b/src/Set/Provider/Sequence.php
index cf4392cc..b0d19dd4 100644
--- a/src/Set/Provider/Sequence.php
+++ b/src/Set/Provider/Sequence.php
@@ -145,7 +145,7 @@ public function exclude(callable $predicate): Set
*
* @template U
*
- * @param callable(list): (U|Seed) $map
+ * @param callable(list): U $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Slice.php b/src/Set/Provider/Slice.php
index 0052b3ab..e1b8f64f 100644
--- a/src/Set/Provider/Slice.php
+++ b/src/Set/Provider/Slice.php
@@ -114,7 +114,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(Util): (V|Seed) $map
+ * @param callable(Util): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Strings.php b/src/Set/Provider/Strings.php
index dd1a2425..964726bd 100644
--- a/src/Set/Provider/Strings.php
+++ b/src/Set/Provider/Strings.php
@@ -204,7 +204,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(string): (V|Seed) $map
+ * @param callable(string): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Strings/Chars.php b/src/Set/Provider/Strings/Chars.php
index f3715e4b..7777e784 100644
--- a/src/Set/Provider/Strings/Chars.php
+++ b/src/Set/Provider/Strings/Chars.php
@@ -142,7 +142,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(non-empty-string): (V|Seed) $map
+ * @param callable(non-empty-string): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Strings/MadeOf.php b/src/Set/Provider/Strings/MadeOf.php
index a3c51aa5..25eb54f2 100644
--- a/src/Set/Provider/Strings/MadeOf.php
+++ b/src/Set/Provider/Strings/MadeOf.php
@@ -130,7 +130,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(string): (V|Seed) $map
+ * @param callable(string): V $map
*
* @return Set
*/
diff --git a/src/Set/Provider/Strings/Unicode.php b/src/Set/Provider/Strings/Unicode.php
index f986761c..78ed2736 100644
--- a/src/Set/Provider/Strings/Unicode.php
+++ b/src/Set/Provider/Strings/Unicode.php
@@ -3191,7 +3191,7 @@ public function exclude(callable $predicate): Set
*
* @template V
*
- * @param callable(string): (V|Seed) $map
+ * @param callable(string): V $map
*
* @return Set
*/
diff --git a/src/Set/Seed.php b/src/Set/Seed.php
index 179e8887..3e421820 100644
--- a/src/Set/Seed.php
+++ b/src/Set/Seed.php
@@ -3,6 +3,8 @@
namespace Innmind\BlackBox\Set;
+use Innmind\BlackBox\Set;
+
/**
* @template-covariant T
*/
@@ -11,10 +13,12 @@ final class Seed
/**
* @psalm-mutation-free
*
- * @param Seed\Map|Seed\FlatMap $implementation
+ * @param \Closure(Implementation): Set $wrap
+ * @param Value $value
*/
private function __construct(
- private Seed\Map|Seed\FlatMap $implementation,
+ private \Closure $wrap,
+ private Value $value,
) {
}
@@ -23,60 +27,31 @@ private function __construct(
* @psalm-pure
* @template A
*
+ * @param \Closure(Implementation): Set $wrap
* @param Value $value
*
* @return self
*/
- public static function of(Value $value): self
- {
- return new self(Seed\Map::of($value));
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): U $map
- *
- * @return self
- */
- public function map(callable $map): self
- {
- return new self($this->implementation->map($map));
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): self $map
- *
- * @return self
- */
- public function flatMap(callable $map): self
- {
- return new self($this->implementation->flatMap($map));
+ public static function of(
+ \Closure $wrap,
+ Value $value,
+ ): self {
+ return new self($wrap, $value);
}
/**
- * @internal
- * @psalm-mutation-free
- *
- * @param \Closure(T): bool $predicate
- *
- * @return ?Dichotomy
+ * @return T
*/
- public function shrink(\Closure $predicate): ?Dichotomy
+ public function unwrap(): mixed
{
- /** @psalm-suppress ImpureMethodCall */
- return $this->implementation->shrink($predicate);
+ return $this->value->unwrap();
}
/**
- * @return T
+ * @return Set
*/
- public function unwrap(): mixed
+ public function toSet(): Set
{
- return $this->implementation->unwrap();
+ return ($this->wrap)(Seeded::implementation($this->value));
}
}
diff --git a/src/Set/Seed/FlatMap.php b/src/Set/Seed/FlatMap.php
deleted file mode 100644
index 07a01d3f..00000000
--- a/src/Set/Seed/FlatMap.php
+++ /dev/null
@@ -1,121 +0,0 @@
- $map
- */
- private function __construct(
- private self|Map $previous,
- private \Closure $map,
- ) {
- }
-
- /**
- * @internal
- * @psalm-pure
- * @template A
- *
- * @param callable(): Seed $map
- *
- * @return self
- */
- public static function of(self|Map $previous, callable $map): self
- {
- return new self($previous, \Closure::fromCallable($map));
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): U $map
- *
- * @return self
- */
- public function map(callable $map): self
- {
- $previous = $this->map;
-
- return new self(
- $this->previous,
- static fn($value) => $previous($value)->map($map),
- );
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): Seed $map
- *
- * @return self
- */
- public function flatMap(callable $map): self
- {
- return new self($this, \Closure::fromCallable($map));
- }
-
- /**
- * @param \Closure(T): bool $predicate
- *
- * @return ?Dichotomy
- */
- public function shrink(\Closure $predicate): ?Dichotomy
- {
- return $this->previousShrink($predicate) ?? $this->collapse()->shrink($predicate);
- }
-
- /**
- * @return T
- */
- public function unwrap(): mixed
- {
- return $this->collapse()->unwrap();
- }
-
- /**
- * @return Seed
- */
- private function collapse(): Seed
- {
- return ($this->map)($this->previous->unwrap());
- }
-
- /**
- * @param \Closure(T): bool $predicate
- *
- * @return ?Dichotomy
- */
- private function previousShrink(\Closure $predicate): ?Dichotomy
- {
- $map = $this->map;
-
- // There's no need to define the immutability of the values here because
- // it's held by the values injected in the new Seeds.
- // No dichotomy because the captured values in the map lambda is shrunk
- // first.
- return $this
- ->previous
- ->shrink($predicate)
- ?->map(
- static fn($strategy) => Value::of(Seed::of($strategy)->flatMap($map))
- ->predicatedOn($predicate),
- );
- }
-}
diff --git a/src/Set/Seed/Map.php b/src/Set/Seed/Map.php
deleted file mode 100644
index 9050ea8d..00000000
--- a/src/Set/Seed/Map.php
+++ /dev/null
@@ -1,118 +0,0 @@
- $value
- *
- * @return self
- */
- public static function of(Value $value): self
- {
- return new self($value, static fn($value): mixed => $value);
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): U $map
- *
- * @return self
- */
- public function map(callable $map): self
- {
- $previous = $this->map;
-
- return new self(
- $this->value,
- static fn($value) => $map($previous($value)),
- );
- }
-
- /**
- * @psalm-mutation-free
- * @template U
- *
- * @param callable(T): Seed $map
- *
- * @return FlatMap
- */
- public function flatMap(callable $map): FlatMap
- {
- /** @psalm-suppress InvalidArgument */
- return FlatMap::of($this, $map);
- }
-
- /**
- * @param \Closure(T): bool $predicate
- *
- * @return ?Dichotomy
- */
- public function shrink(\Closure $predicate): ?Dichotomy
- {
- $shrunk = $this->value->shrink();
-
- if (\is_null($shrunk)) {
- return null;
- }
-
- // There's no need to define the immutability of the values here because
- // it's held by the values injected in the new Seeds.
- // No dichotomy because the captured values in the configure lambda is
- // shrunk first
- $a = Value::of(Seed::of($shrunk->a())->map($this->map))
- ->predicatedOn($predicate);
- $b = Value::of(Seed::of($shrunk->b())->map($this->map))
- ->predicatedOn($predicate);
-
- // If one of the strategies is not acceptable then we remove it and it
- // will de defaulted to the parent value. And if both of them are not
- // acceptable then the shrinking stops.
- if (!$a->acceptable()) {
- $a = null;
- }
-
- if (!$b->acceptable()) {
- $b = null;
- }
-
- return Dichotomy::of($a, $b);
- }
-
- /**
- * @return T
- */
- public function unwrap(): mixed
- {
- return ($this->map)($this->value->unwrap());
- }
-}
diff --git a/src/Set/Seeded.php b/src/Set/Seeded.php
new file mode 100644
index 00000000..8a5cad49
--- /dev/null
+++ b/src/Set/Seeded.php
@@ -0,0 +1,52 @@
+
+ */
+final class Seeded implements Implementation
+{
+ /**
+ * @param Value $value
+ */
+ private function __construct(
+ private Value $value,
+ ) {
+ }
+
+ #[\Override]
+ public function __invoke(
+ Random $random,
+ \Closure $predicate,
+ ): \Generator {
+ $value = $this->value->predicatedOn($predicate);
+
+ if (!$value->acceptable()) {
+ return;
+ }
+
+ while (true) {
+ yield $value;
+ }
+ }
+
+ /**
+ * @internal
+ *
+ * @template A
+ *
+ * @param Value $value
+ *
+ * @return self
+ */
+ public static function implementation(Value $value): self
+ {
+ return new self($value);
+ }
+}
diff --git a/src/Set/Value.php b/src/Set/Value.php
index 7fcf65a8..5f96091d 100644
--- a/src/Set/Value.php
+++ b/src/Set/Value.php
@@ -15,14 +15,12 @@
*/
final class Value
{
- private ?Seed $seed = null;
-
/**
* @psalm-mutation-free
*
* @param Map $map
* @param \Closure(mixed): bool $predicate
- * @param T|Seed $unwrapped
+ * @param T $unwrapped
*/
private function __construct(
private mixed $source,
@@ -38,7 +36,7 @@ private function __construct(
* @psalm-pure
* @template V
*
- * @param V|Seed $value
+ * @param V $value
*
* @return self
*/
@@ -103,7 +101,7 @@ public function predicatedOn(callable $predicate): self
* @psalm-mutation-free
* @template V
*
- * @param callable(T): (V|Seed) $map
+ * @param callable(T): V $map
*
* @return self
*/
@@ -193,9 +191,11 @@ public function acceptable(): bool
*/
public function shrink(): ?Dichotomy
{
- $dichotomy = ($this->shrink)?->__invoke($this) ?? $this->seed?->shrink($this->predicate);
+ if (\is_null($this->shrink)) {
+ return null;
+ }
- return $dichotomy?->default($this->withoutShrinking());
+ return ($this->shrink)($this)?->default($this->withoutShrinking());
}
/**
@@ -203,19 +203,6 @@ public function shrink(): ?Dichotomy
*/
public function unwrap()
{
- $value = $this->unwrapped;
-
- // This is not ideal to hide the seeded value this way and to hijack
- // the shrinking system in self::shrinkable() and self::shrink() as it
- // complexifies the understanding of what's happening. Because now the
- // filtering can happen in 2 places.
- // Until a better idea comes along, this will stay this way.
- if ($value instanceof Seed) {
- $this->seed = $value;
- /** @var T */
- $value = $value->unwrap();
- }
-
- return $value;
+ return $this->unwrapped;
}
}
diff --git a/src/Set/Value/Map.php b/src/Set/Value/Map.php
index 784c5503..37e518ce 100644
--- a/src/Set/Value/Map.php
+++ b/src/Set/Value/Map.php
@@ -3,11 +3,6 @@
namespace Innmind\BlackBox\Set\Value;
-use Innmind\BlackBox\Set\{
- Seed,
- Value,
-};
-
/**
* @internal
* @template-covariant T
@@ -17,7 +12,7 @@ final class Map
/**
* @psalm-mutation-free
*
- * @param \Closure(mixed): (T|Seed) $map
+ * @param \Closure(mixed): T $map
*/
private function __construct(
private \Closure $map,
@@ -25,7 +20,7 @@ private function __construct(
}
/**
- * @return T|Seed
+ * @return T
*/
public function __invoke(mixed $source): mixed
{
@@ -45,7 +40,7 @@ public static function noop(): self
* @psalm-mutation-free
* @template V
*
- * @param callable(T): (V|Seed) $map
+ * @param callable(T): V $map
*
* @return self
*/
@@ -53,23 +48,8 @@ public function with(callable $map): self
{
$previous = $this->map;
- return new self(static function(mixed $source) use ($map, $previous): mixed {
- $value = $previous($source);
-
- if ($value instanceof Seed) {
- return $value->flatMap(static function($value) use ($map) {
- /** @var T $value */
- $mapped = $map($value);
-
- if ($mapped instanceof Seed) {
- return $mapped;
- }
-
- return Seed::of(Value::of($mapped));
- });
- }
-
- return $map($value);
- });
+ return new self(
+ static fn(mixed $source) => $map($previous($source)),
+ );
}
}