Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  specify next release
  add OrPredicate and AndPredicate
  add Set::match()
  • Loading branch information
Baptouuuu committed Nov 5, 2023
2 parents dad4c41 + e8659af commit 9ba8332
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 2 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 5.2.0 - 2023-11-05

### Added

- `Innmind\Immutable\Set::match()`
- `Innmind\Immutable\Predicate\OrPredicate`
- `Innmind\Immutable\Predicate\AndPredicate`
- `Innmind\Immutable\Predicate\Instance::or()`
- `Innmind\Immutable\Predicate\Instance::and()`

## 5.1.0 - 2023-10-11

### Changed
Expand Down
19 changes: 19 additions & 0 deletions docs/SET.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,25 @@ $firstOdd = $set->find(fn($i) => $i % 2 === 1);
$firstOdd; // could contain 9 or 11, because there is no ordering
```

## `->match()`

This is a similar approach to pattern matching allowing you to decompose a set by accessing the first element and the rest of the set.

```php
function sum(Set $ints): int
{
return $ints->match(
fn(int $head, Set $tail) => $head + sum($tail),
fn() => 0,
);
}

$result = sum(Set::of(1, 2, 3, 4));
$result; // 10
```

> **Warning** for lazy sets bear in mind that the values will be kept in memory while the first call to `->match` didn't return.
## `->matches()`

Check if all the elements of the set matches the given predicate.
Expand Down
52 changes: 52 additions & 0 deletions proofs/predicate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
declare(strict_types = 1);

use Innmind\Immutable\Predicate\Instance;

return static function() {
yield test(
'Or predicate',
static function($assert) {
$array = new \SplFixedArray;

$assert->true(
Instance::of(\Countable::class)
->or(Instance::of(\stdClass::class))
($array),
);
$assert->true(
Instance::of(\stdClass::class)
->or(Instance::of(\Countable::class))
($array),
);
$assert->false(
Instance::of(\Throwable::class)
->or(Instance::of(\Unknown::class))
($array),
);
},
);

yield test(
'And predicate',
static function($assert) {
$array = new \SplFixedArray;

$assert->true(
Instance::of(\Countable::class)
->and(Instance::of(\Traversable::class))
($array),
);
$assert->false(
Instance::of(\Throwable::class)
->and(Instance::of(\Countable::class))
($array),
);
$assert->false(
Instance::of(\Countable::class)
->and(Instance::of(\Throwable::class))
($array),
);
},
);
};
37 changes: 37 additions & 0 deletions proofs/set.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
declare(strict_types = 1);

use Innmind\Immutable\{
Set,
Sequence,
};
use Innmind\BlackBox\Set as DataSet;

return static function() {
yield proof(
'Set::match()',
given(
DataSet\Type::any(),
DataSet\Sequence::of(DataSet\Type::any())
->map(static fn($values) => Sequence::of(...$values)->distinct()->toList()),
)->filter(static fn($first, $rest) => !\in_array($first, $rest, true)),
static function($assert, $first, $rest) {
$assert->same(
$first,
Set::of()->match(
static fn() => false,
static fn() => $first,
),
);

$packed = Set::of($first, ...$rest)->match(
static fn($first, $rest) => [$first, $rest],
static fn() => null,
);

$assert->not()->null($packed);
$assert->same($first, $packed[0]);
$assert->same($rest, $packed[1]->toList());
},
);
};
50 changes: 50 additions & 0 deletions src/Predicate/AndPredicate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
declare(strict_types = 1);

namespace Innmind\Immutable\Predicate;

use Innmind\Immutable\Predicate;

/**
* @psalm-immutable
* @template A
* @template B
* @implements Predicate<A&B>
*/
final class AndPredicate implements Predicate
{
/** @var Predicate<A> */
private Predicate $a;
/** @var Predicate<B> */
private Predicate $b;

/**
* @param Predicate<A> $a
* @param Predicate<B> $b
*/
private function __construct(Predicate $a, Predicate $b)
{
$this->a = $a;
$this->b = $b;
}

public function __invoke(mixed $value): bool
{
return ($this->a)($value) && ($this->b)($value);
}

/**
* @psalm-pure
* @template T
* @template V
*
* @param Predicate<T> $a
* @param Predicate<V> $b
*
* @return self<T, V>
*/
public static function of(Predicate $a, Predicate $b): self
{
return new self($a, $b);
}
}
24 changes: 24 additions & 0 deletions src/Predicate/Instance.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,28 @@ public static function of(string $class): self
{
return new self($class);
}

/**
* @template T
*
* @param Predicate<T> $predicate
*
* @return OrPredicate<A, T>
*/
public function or(Predicate $predicate): OrPredicate
{
return OrPredicate::of($this, $predicate);
}

/**
* @template T
*
* @param Predicate<T> $predicate
*
* @return AndPredicate<A, T>
*/
public function and(Predicate $predicate): AndPredicate
{
return AndPredicate::of($this, $predicate);
}
}
50 changes: 50 additions & 0 deletions src/Predicate/OrPredicate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
declare(strict_types = 1);

namespace Innmind\Immutable\Predicate;

use Innmind\Immutable\Predicate;

/**
* @psalm-immutable
* @template A
* @template B
* @implements Predicate<A|B>
*/
final class OrPredicate implements Predicate
{
/** @var Predicate<A> */
private Predicate $a;
/** @var Predicate<B> */
private Predicate $b;

/**
* @param Predicate<A> $a
* @param Predicate<B> $b
*/
private function __construct(Predicate $a, Predicate $b)
{
$this->a = $a;
$this->b = $b;
}

public function __invoke(mixed $value): bool
{
return ($this->a)($value) || ($this->b)($value);
}

/**
* @psalm-pure
* @template T
* @template V
*
* @param Predicate<T> $a
* @param Predicate<V> $b
*
* @return self<T, V>
*/
public static function of(Predicate $a, Predicate $b): self
{
return new self($a, $b);
}
}
5 changes: 3 additions & 2 deletions src/Sequence/Implementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,10 @@ public function find(callable $predicate): Maybe;

/**
* @template R
* @template C of Sequence<T>|Set<T>
*
* @param callable(self<T>): Sequence<T> $wrap
* @param callable(T, Sequence<T>): R $match
* @param callable(self<T>): C $wrap
* @param callable(T, C): R $match
* @param callable(): R $empty
*
* @return R
Expand Down
18 changes: 18 additions & 0 deletions src/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,24 @@ public function find(callable $predicate): Maybe
return $this->implementation->find($predicate);
}

/**
* @template R
*
* @param callable(T, self<T>): R $match
* @param callable(): R $empty
*
* @return R
*/
public function match(callable $match, callable $empty)
{
/** @psalm-suppress MixedArgument For some reason Psalm no longer recognize the type of $first */
return $this->implementation->sequence()->match(
static fn($sequence) => new self(new Set\Primitive($sequence)),
static fn($first, $rest) => $match($first, $rest),
$empty,
);
}

/**
* @param callable(T): bool $predicate
*/
Expand Down

0 comments on commit 9ba8332

Please sign in to comment.