Skip to content

Commit b6bcd01

Browse files
committed
Add proveUnion function
1 parent 9c9458b commit b6bcd01

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"src/Fp/Functions/Collection/Tail.php",
8888
"src/Fp/Functions/Collection/Zip.php",
8989
"src/Fp/Functions/Collection/MkString.php",
90+
"src/Fp/Functions/Evidence/ProveUnion.php",
9091
"src/Fp/Functions/Evidence/ProveNull.php",
9192
"src/Fp/Functions/Evidence/ProveArray.php",
9293
"src/Fp/Functions/Evidence/ProveBool.php",
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Fp\Evidence;
6+
7+
use Closure;
8+
use Fp\Functional\Option\Option;
9+
10+
use function Fp\Collection\firstMap;
11+
12+
/**
13+
* Prove that subject is one of given types
14+
*
15+
* ```php
16+
* >>> proveUnion(1, [proveString(...), proveInt(...)]);
17+
* => Some(1)
18+
* >>> proveUnion('str', [proveString(...), proveInt(...)]);
19+
* => Some('str')
20+
* >>> proveUnion(0.42, [proveString(...), proveInt(...)]);
21+
* => None
22+
* ```
23+
*
24+
* @template T
25+
*
26+
* @param non-empty-list<Closure(mixed): Option<T>> $evidences
27+
* @return Option<T>
28+
*/
29+
function proveUnion(mixed $subject, array $evidences): Option
30+
{
31+
return firstMap($evidences, fn(Closure $prove) => $prove($subject));
32+
}
33+
34+
/**
35+
* Curried version of {@see proveUnion()}
36+
*
37+
* ```php
38+
* >>> union([proveString(...), proveInt(...)])(1);
39+
* => Some(1)
40+
* >>> union([proveString(...), proveInt(...)])('str');
41+
* => Some('str')
42+
* >>> union([proveString(...), proveInt(...)])(0.42);
43+
* => None
44+
* ```
45+
*
46+
* @template T
47+
*
48+
* @param non-empty-list<Closure(mixed): Option<T>> $evidences
49+
* @return Closure(mixed): Option<T>
50+
*/
51+
function union(array $evidences): Closure
52+
{
53+
return fn(mixed $subject) => proveUnion($subject, $evidences);
54+
}
55+
56+
/**
57+
* Curried and varargs version of {@see proveUnion()}
58+
*
59+
* ```php
60+
* >>> unionT(proveString(...), proveInt(...))(1);
61+
* => Some(1)
62+
* >>> unionT(proveString(...), proveInt(...))('str');
63+
* => Some('str')
64+
* >>> unionT(proveString(...), proveInt(...))(0.42);
65+
* => None
66+
* ```
67+
*
68+
* @template T
69+
*
70+
* @param Closure(mixed): Option<T> $evidence
71+
* @param Closure(mixed): Option<T> ...$evidences
72+
* @return Closure(mixed): Option<T>
73+
*/
74+
function unionT(Closure $evidence, Closure ...$evidences): Closure
75+
{
76+
return fn(mixed $subject) => proveUnion($subject, [$evidence, ...$evidences]);
77+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Runtime\Functions\Evidence;
6+
7+
use Fp\Functional\Option\Option;
8+
use PHPUnit\Framework\TestCase;
9+
10+
use function Fp\Evidence\proveInt;
11+
use function Fp\Evidence\proveNull;
12+
use function Fp\Evidence\proveString;
13+
use function Fp\Evidence\proveUnion;
14+
use function Fp\Evidence\union;
15+
use function Fp\Evidence\unionT;
16+
17+
final class ProveUnionTest extends TestCase
18+
{
19+
public function testProveUnion(): void
20+
{
21+
$evidences = [
22+
proveInt(...),
23+
proveString(...),
24+
proveNull(...),
25+
];
26+
27+
$this->assertEquals(Option::none(), proveUnion(42.00, $evidences));
28+
$this->assertEquals(Option::none(), proveUnion([42], $evidences));
29+
$this->assertEquals(Option::some(42), proveUnion(42, $evidences));
30+
$this->assertEquals(Option::some('str'), proveUnion('str', $evidences));
31+
$this->assertEquals(Option::some(null), proveUnion(null, $evidences));
32+
}
33+
34+
public function testUnion(): void
35+
{
36+
$evidences = [
37+
proveInt(...),
38+
proveString(...),
39+
proveNull(...),
40+
];
41+
42+
$this->assertEquals(Option::none(), union($evidences)(42.00));
43+
$this->assertEquals(Option::none(), union($evidences)([42]));
44+
$this->assertEquals(Option::some(42), union($evidences)(42));
45+
$this->assertEquals(Option::some('str'), union($evidences)('str'));
46+
$this->assertEquals(Option::some(null), union($evidences)(null));
47+
}
48+
49+
public function testUnionT(): void
50+
{
51+
$evidences = [
52+
proveInt(...),
53+
proveString(...),
54+
proveNull(...),
55+
];
56+
57+
$this->assertEquals(Option::none(), unionT(...$evidences)(42.00));
58+
$this->assertEquals(Option::none(), unionT(...$evidences)([42]));
59+
$this->assertEquals(Option::some(42), unionT(...$evidences)(42));
60+
$this->assertEquals(Option::some('str'), unionT(...$evidences)('str'));
61+
$this->assertEquals(Option::some(null), unionT(...$evidences)(null));
62+
}
63+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Static\Functions\Evidence;
6+
7+
use Fp\Functional\Option\Option;
8+
9+
use function Fp\Evidence\proveInt;
10+
use function Fp\Evidence\proveString;
11+
use function Fp\Evidence\proveUnion;
12+
use function Fp\Evidence\union;
13+
use function Fp\Evidence\unionT;
14+
15+
final class ProveUnionStaticTest
16+
{
17+
/**
18+
* @return Option<int|string>
19+
*/
20+
public function proveUnion(mixed $value): Option
21+
{
22+
return proveUnion($value, [
23+
proveInt(...),
24+
proveString(...),
25+
]);
26+
}
27+
28+
/**
29+
* @return Option<int|string>
30+
*/
31+
public function union(mixed $value): Option
32+
{
33+
return union([
34+
proveInt(...),
35+
proveString(...),
36+
])($value);
37+
}
38+
39+
/**
40+
* @return Option<int|string>
41+
*/
42+
public function unionT(mixed $value): Option
43+
{
44+
return unionT(proveInt(...), proveString(...))($value);
45+
}
46+
}

0 commit comments

Comments
 (0)