Skip to content

Commit

Permalink
Requested changes, static analysis tests
Browse files Browse the repository at this point in the history
  • Loading branch information
devnix committed Dec 17, 2023
1 parent d8f5cbd commit fdd661b
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 30 deletions.
46 changes: 24 additions & 22 deletions src/Psl/Option/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Closure;
use Psl\Comparison;
use Psl\Type;

/**
* @template T
Expand Down Expand Up @@ -312,11 +313,15 @@ public function equals(mixed $other): bool
*
* @note: If an element is `None`, the corresponding element in the resulting tuple will be `None`.
*
* @template U
* @template Tu
*
* @param Option<U> $other The other `Option` to zip with.
* @param Option<Tu> $other The other `Option` to zip with.
*
* @return Option<array{T, U}> The resulting `Option` containing the combined tuple or `None`.
* @return (
* T is never
* ? Option<never>
* : (Tu is never ? Option<never> : Option<array{T, Tu}>)
* )
*/
public function zip(Option $other): Option
{
Expand All @@ -329,13 +334,13 @@ public function zip(Option $other): Option
* Applies the provided closure to the value contained in this `Option` and the value contained in the $other `Option`,
* and returns a new `Option` containing the result of the closure.
*
* @template U
* @template Tv
* @template Tu
* @template Tr
*
* @param Option<U> $other The Option to zip with.
* @param (Closure(T, U): Tv) $closure The closure to apply to the values.
* @param Option<Tu> $other The Option to zip with.
* @param (Closure(T, Tu): Tr) $closure The closure to apply to the values.
*
* @return Option<Tv> The new `Option` containing the result of applying the closure to the values,
* @return Option<Tr> The new `Option` containing the result of applying the closure to the values,
* or `None` if either this or the $other `Option is `None`.
*/
public function zipWith(Option $other, Closure $closure): Option
Expand All @@ -344,37 +349,34 @@ public function zipWith(Option $other, Closure $closure): Option
/** @param T $a */
static function ($a) use ($other, $closure) {
return $other->map(
/** @param U $b */
/** @param Tu $b */
static fn ($b) => $closure($a, $b)
);
}
);
}

/**
* @template OValue1
* @template OValue2
* @template Tv
* @template Tr
*
* @psalm-if-this-is Option<array{Tv, Tr}>
*
* @psalm-if-this-is Option<array{OValue1, OValue2}>
* @throws Type\Exception\AssertException
*
* @return array{Option<OValue1>, Option<OValue2>}
* @return array{Option<Tv>, Option<Tr>}
*/
public function unzip(): array
{
if ($this->option === null) {
return [none(), none()];
}

/** @psalm-suppress DocblockTypeContradiction we want this check in runtime */
if (!is_array($this->option[0])) {
return [none(), none()];
}

if (!array_key_exists(0, $this->option[0]) || !array_key_exists(1, $this->option[0])) {
return [none(), none()];
}
// Assertion done in a separate variable to avoid Psalm inferring the type of $this->option as mixed
$option = $this->option[0];
Type\shape([Type\mixed(), Type\mixed()])->assert($option);

[$a, $b] = $this->option[0];
[$a, $b] = $option;

return [some($a), some($b)];
}
Expand Down
4 changes: 2 additions & 2 deletions tests/fixture/Point.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
final class Point
{
public function __construct(
public readonly float $x,
public readonly float $y,
public readonly int $x,
public readonly int $y,
) {
}
}
76 changes: 76 additions & 0 deletions tests/static-analysis/Option/zip.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

use Psl\Option;
use Psl\Type;

/**
* @return Option\Option<never>
*/
function test_partial_none_tuple_1(): Option\Option
{
return Option\none()->zip(Option\some(1));
}

/**
* @return Option\Option<never>
*/
function test_partial_none_tuple_2(): Option\Option
{
return Option\some(1)->zip(Option\none());
}

/**
* @throws Type\Exception\AssertException
*
* @return array{Option\Option<never>, Option\Option<never>}
*/
function test_partial_none_unzip_1(): array
{
return test_partial_none_tuple_1()->unzip();
}

/**
* @return Option\Option<array{int, string}>
*/
function test_some_zip(): Option\Option
{
return Option\some(1)->zip(Option\some('2'));
}

/**
* @throws Type\Exception\AssertException
*
* @return array{Option\Option<never>, Option\Option<never>}
*/
function test_partial_none_unzip_2(): array
{
return test_partial_none_tuple_2()->unzip();
}

/**
* @throws Type\Exception\AssertException
*
* @return array{Option\Option<int>, Option\Option<string>}
*/
function test_some_unzip(): array
{
return test_some_zip()->unzip();
}

/**
* @return Option\Option<int>
*/
function test_some_zip_with()
{
return Option\some(1)->zipWith(Option\some('2'), static fn($a, $b) => $a + (int) $b);
}

/**
* @return Option\Option<string>
*/
function test_some_zip_with_2()
{
return Option\some(1)->zipWith(Option\some('2'), static fn($a, $b) => $b);
}
3 changes: 0 additions & 3 deletions tests/unit/Option/NoneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,6 @@ public function testUnzip(Option\Option $option): void
private function provideTestUnzip(): iterable
{
yield [Option\none()];
yield [Option\some(1)];
yield [Option\some([])];
yield [Option\some(['foo'])];
yield [Option\none()->zip(Option\none())];
yield [Option\none()->zip(Option\some(1))];
yield [Option\some(1)->zip(Option\none())];
Expand Down
23 changes: 20 additions & 3 deletions tests/unit/Option/SomeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Psl\Comparison\Equable;
use Psl\Comparison\Order;
use Psl\Option;
use Psl\Type;
use Psl\Tests\Fixture;

final class SomeTest extends TestCase
Expand Down Expand Up @@ -142,12 +143,12 @@ public function testZip(): void

public function testZipWith(): void
{
$x = Option\some(17.5);
$y = Option\some(42.7);
$x = Option\some(17);
$y = Option\some(42);

$point = $x->zipWith($y, static fn($a, $b) => new Fixture\Point($a, $b));

static::assertTrue(Option\some(new Fixture\Point(17.5, 42.7))->equals($point));
static::assertTrue(Option\some(new Fixture\Point(17, 42))->equals($point));
}

/**
Expand All @@ -167,4 +168,20 @@ private function provideTestUnzip(): iterable
yield [Option\some(1)->zip(Option\some('hi')), 1, 'hi'];
yield [Option\some([true, false]), true, false];
}

/**
* @dataProvider provideTestUnzipAssertionException
*/
public function testUnzipAssertionException(Option\Option $option): void
{
static::expectException(Type\Exception\AssertException::class);
$option->unzip();
}

private function provideTestUnzipAssertionException(): iterable
{
yield [Option\some(null)];
yield [Option\some(1)];
yield [Option\some([true])];
}
}

0 comments on commit fdd661b

Please sign in to comment.