├── src
├── Exception
│ ├── LogicOptionException.php
│ └── RuntimeOptionException.php
├── Option
│ └── functions.php
├── OptionFactory.php
├── None.php
├── Some.php
└── Option.php
├── .php-cs-fixer.php
├── composer.json
├── phpunit.xml
├── LICENSE
└── README.md
/src/Exception/LogicOptionException.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type\Exception;
15 |
16 | class LogicOptionException extends \LogicException
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/src/Exception/RuntimeOptionException.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type\Exception;
15 |
16 | class RuntimeOptionException extends \RuntimeException
17 | {
18 | }
19 |
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 |
7 |
8 | For the full copyright and license information, please view the LICENSE
9 | file that was distributed with this source code.
10 | EOF;
11 |
12 | $finder = (new PhpCsFixer\Finder())
13 | ->in(__DIR__.'/src')
14 | ;
15 |
16 | return (new PhpCsFixer\Config())
17 | ->setRules([
18 | '@Symfony' => true,
19 | 'header_comment' => ['header' => $header],
20 | 'declare_strict_types' => true,
21 | ])
22 | ->setFinder($finder)
23 | ;
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yceruto/option-type",
3 | "description": "An Option type that represents an optional value",
4 | "type": "library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Yonel Ceruto",
9 | "email": "open@yceruto.dev"
10 | }
11 | ],
12 | "require": {
13 | "php": ">=8.2"
14 | },
15 | "require-dev": {
16 | "phpunit/phpunit": "^11.1",
17 | "phpstan/phpstan": "^1.11",
18 | "friendsofphp/php-cs-fixer": "^3.54"
19 | },
20 | "autoload": {
21 | "psr-4": {
22 | "Std\\Type\\": "src/"
23 | },
24 | "files": ["src/Option/functions.php"]
25 | },
26 | "autoload-dev": {
27 | "psr-4": {
28 | "Std\\Type\\Tests\\": "tests/"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | tests
15 |
16 |
17 |
18 |
19 |
20 | src
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/Option/functions.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type\Option;
15 |
16 | use Std\Type\None;
17 | use Std\Type\Some;
18 |
19 | if (!\function_exists('some')) {
20 | /**
21 | * Some value.
22 | *
23 | * @template T
24 | *
25 | * @param T $value A value of type T
26 | *
27 | * @return Some Some option
28 | */
29 | function some(mixed $value): Some
30 | {
31 | return new Some($value);
32 | }
33 | }
34 |
35 | if (!\function_exists('none')) {
36 | /**
37 | * No value.
38 | */
39 | function none(): None
40 | {
41 | return new None();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024-present Yonel Ceruto González
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/OptionFactory.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type;
15 |
16 | final readonly class OptionFactory
17 | {
18 | /**
19 | * Returns an `Option` with the specified value.
20 | *
21 | * If the value is `null`, returns `None`, otherwise returns `Some`.
22 | *
23 | * Examples
24 | * ```
25 | * $x = Option::from(2);
26 | * assert($x->isSome(), 'Expected $x to be Some.');
27 | *
28 | * $x = Option::from(null);
29 | * assert($x->isNone(), 'Expected $x to be None.');
30 | * ```
31 | *
32 | * @template T of mixed
33 | *
34 | * @param T $value A value of type T
35 | *
36 | * @return None|Some Some or None Option
37 | */
38 | public static function from(mixed $value): None|Some
39 | {
40 | return null === $value ? new None() : new Some($value);
41 | }
42 |
43 | private function __construct()
44 | {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/None.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type;
15 |
16 | use Std\Type\Exception\RuntimeOptionException;
17 |
18 | /**
19 | * No value.
20 | *
21 | * Also see {@see none()} for a shorter way to create a None Option.
22 | *
23 | * @implements Option
24 | */
25 | final readonly class None implements Option
26 | {
27 | public function isSome(): bool
28 | {
29 | return false;
30 | }
31 |
32 | public function isNone(): bool
33 | {
34 | return true;
35 | }
36 |
37 | public function match(callable $some, callable $none): mixed
38 | {
39 | return $none();
40 | }
41 |
42 | public function expect(string $message): mixed
43 | {
44 | throw new RuntimeOptionException($message);
45 | }
46 |
47 | public function unwrap(): mixed
48 | {
49 | throw new RuntimeOptionException(sprintf('Calling unwrap() method on a %s option. Check isNone() method first or use a fallback method instead.', self::class));
50 | }
51 |
52 | public function unwrapOr(mixed $default): mixed
53 | {
54 | return $default;
55 | }
56 |
57 | public function unwrapOrElse(callable $fn): mixed
58 | {
59 | return $fn();
60 | }
61 |
62 | public function unwrapOrThrow(\Throwable $error): mixed
63 | {
64 | throw $error;
65 | }
66 |
67 | public function map(callable $fn): self
68 | {
69 | return $this;
70 | }
71 |
72 | public function mapOr(callable $fn, mixed $default): mixed
73 | {
74 | return $default;
75 | }
76 |
77 | public function mapOrElse(callable $fn, callable $default): mixed
78 | {
79 | return $default();
80 | }
81 |
82 | public function or(Option $option): Option
83 | {
84 | return $option;
85 | }
86 |
87 | public function orElse(callable $fn): Option
88 | {
89 | return $fn();
90 | }
91 |
92 | public function xor(Option $option): Option
93 | {
94 | return $option instanceof self ? $this : $option;
95 | }
96 |
97 | public function and(Option $option): Option
98 | {
99 | return $this;
100 | }
101 |
102 | public function andThen(callable $fn): self
103 | {
104 | return $this;
105 | }
106 |
107 | public function iterate(): iterable
108 | {
109 | return [];
110 | }
111 |
112 | public function filter(callable $predicate): self
113 | {
114 | return $this;
115 | }
116 |
117 | public function equals(Option $option): bool
118 | {
119 | return $option instanceof self;
120 | }
121 |
122 | public function flatten(): self
123 | {
124 | return $this;
125 | }
126 |
127 | public function clone(): self
128 | {
129 | return clone $this;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Option type
2 |
3 | [](https://packagist.org/packages/yceruto/option-type)
4 | [](https://packagist.org/packages/yceruto/option-type)
5 | [](https://packagist.org/packages/yceruto/option-type)
6 | [](https://packagist.org/packages/yceruto/option-type)
7 |
8 | The `Option` type represents a value that might or might not be there. It's all about
9 | null safety in PHP!
10 |
11 | > [!NOTE]
12 | > Inspired by [Rust's Option type](https://doc.rust-lang.org/std/option/) and other
13 | > languages like Scala, Swift, F#, etc.
14 |
15 | ## Installation
16 |
17 | ```bash
18 | composer require yceruto/option-type
19 | ```
20 |
21 | ## Handling the presence or absence of a value with `null`
22 |
23 | In PHP, denoting the absence of a value is done with `null`, e.g. when a `divide`
24 | function returns `null` if the divisor is `0`.
25 |
26 | ```php
27 | function divide(int $dividend, int $divisor): ?int
28 | {
29 | if (0 === $divisor) {
30 | return null;
31 | }
32 |
33 | return intdiv($dividend, $divisor);
34 | }
35 |
36 | function success(int $result): string {
37 | return sprintf('Result: %d', $result);
38 | }
39 |
40 | $result = divide(10, 2);
41 |
42 | echo success($result);
43 | ```
44 |
45 | Can you spot the issue in this code? Apparently, everything is fine until you try to
46 | divide by zero. The function will return `null`, and the `success()` function will throw
47 | a `TypeError` because it expects an `int` value, not `null`.
48 |
49 | The issue with this approach is that it's too easy to overlook checking if the value is
50 | `null`, leading to runtime errors, and this is where the `Option` type comes in handy: it
51 | always forces you to deal with the `null` case.
52 |
53 | ## Handling the presence or absence of a value with `Option`
54 |
55 | Options often work with pattern matching to check if there’s a value and act accordingly,
56 | always making sure to handle the `null` case.
57 |
58 | ```php
59 | use Std\Type\Option;
60 | use function Std\Type\Option\none;
61 | use function Std\Type\Option\some;
62 |
63 | /**
64 | * @return Option
65 | */
66 | function divide(int $dividend, int $divisor): Option
67 | {
68 | if (0 === $divisor) {
69 | return none();
70 | }
71 |
72 | return some(intdiv($dividend, $divisor));
73 | }
74 |
75 | function success(int $result): string {
76 | return sprintf('Result: %d', $result);
77 | }
78 |
79 | // The return value of the function is an Option
80 | $result = divide(10, 2);
81 |
82 | // Pattern match to retrieve the value
83 | echo $result->match(
84 | // The division was valid
85 | some: fn (int $v) => success($v),
86 | // The division was invalid
87 | none: fn () => 'Division by zero!',
88 | );
89 | ```
90 |
91 | > [!TIP]
92 | >You can use the functions `some()` and `none()` as quick ways to create an `Option`
93 | >instance. `some()` is just like `new Some()`, meaning it includes a value, while
94 | >`none()` is the same as `new None()`, indicating it is missing a value.
95 |
96 | ## Documentation
97 |
98 | * [API Reference](docs/api_reference.md)
99 | * [Examples](docs/examples.md)
100 |
101 | ## License
102 |
103 | This software is published under the [MIT License](LICENSE)
104 |
--------------------------------------------------------------------------------
/src/Some.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type;
15 |
16 | use Std\Type\Exception\LogicOptionException;
17 |
18 | /**
19 | * Some value.
20 | *
21 | * Also see {@see some()} for a shorter way to create a Some Option.
22 | *
23 | * @template T
24 | *
25 | * @implements Option
26 | */
27 | final readonly class Some implements Option
28 | {
29 | private mixed $value;
30 |
31 | /**
32 | * @param T $value A value of type T
33 | */
34 | public function __construct(mixed $value)
35 | {
36 | if (null === $value) {
37 | throw new LogicOptionException(sprintf('Cannot create %s option with a null value, use %s instead.', self::class, None::class));
38 | }
39 |
40 | $this->value = $value;
41 | }
42 |
43 | public function isSome(): bool
44 | {
45 | return true;
46 | }
47 |
48 | public function isNone(): bool
49 | {
50 | return false;
51 | }
52 |
53 | public function match(callable $some, callable $none): mixed
54 | {
55 | return $some($this->value);
56 | }
57 |
58 | public function expect(string $message): mixed
59 | {
60 | return $this->value;
61 | }
62 |
63 | public function unwrap(): mixed
64 | {
65 | return $this->value;
66 | }
67 |
68 | public function unwrapOr(mixed $default): mixed
69 | {
70 | return $this->value;
71 | }
72 |
73 | public function unwrapOrElse(callable $fn): mixed
74 | {
75 | return $this->value;
76 | }
77 |
78 | public function unwrapOrThrow(\Throwable $error): mixed
79 | {
80 | return $this->value;
81 | }
82 |
83 | public function map(callable $fn): Option
84 | {
85 | if (null === $value = $fn($this->value)) {
86 | return new None();
87 | }
88 |
89 | return new self($value);
90 | }
91 |
92 | public function mapOr(callable $fn, mixed $default): mixed
93 | {
94 | return $fn($this->value);
95 | }
96 |
97 | public function mapOrElse(callable $fn, callable $default): mixed
98 | {
99 | return $fn($this->value);
100 | }
101 |
102 | /**
103 | * @return Some
104 | */
105 | public function or(Option $option): self
106 | {
107 | return $this;
108 | }
109 |
110 | /**
111 | * @return Some
112 | */
113 | public function orElse(callable $fn): self
114 | {
115 | return $this;
116 | }
117 |
118 | public function xor(Option $option): Option
119 | {
120 | return $option instanceof self ? new None() : $this;
121 | }
122 |
123 | public function and(Option $option): Option
124 | {
125 | return $option;
126 | }
127 |
128 | public function andThen(callable $fn): Option
129 | {
130 | return $fn($this->value);
131 | }
132 |
133 | public function iterate(): iterable
134 | {
135 | return new \ArrayIterator((array) $this->value);
136 | }
137 |
138 | /**
139 | * @return None|Some
140 | */
141 | public function filter(callable $predicate): Option
142 | {
143 | if ($predicate($this->value)) {
144 | return $this;
145 | }
146 |
147 | return new None();
148 | }
149 |
150 | public function equals(Option $option): bool
151 | {
152 | return $option instanceof self && $this->value === $option->value;
153 | }
154 |
155 | public function flatten(): Option
156 | {
157 | if ($this->value instanceof Option) {
158 | return $this->value;
159 | }
160 |
161 | throw new LogicOptionException(sprintf('Calling %s() method on a non-Option value. Unexpected "%s" type.', __METHOD__, get_debug_type($this->value)));
162 | }
163 |
164 | /**
165 | * @return Some
166 | */
167 | public function clone(): self
168 | {
169 | return clone $this;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/Option.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Std\Type;
15 |
16 | use Std\Type\Exception\LogicOptionException;
17 | use Std\Type\Exception\RuntimeOptionException;
18 |
19 | use function Std\Type\Option\some;
20 |
21 | /**
22 | * The Option type represents an optional value: every Option
23 | * is either Some and contains a value, or None, and does not.
24 | *
25 | * @template T
26 | */
27 | interface Option
28 | {
29 | /**
30 | * Returns `true` if the option is a {@see Some} value.
31 | *
32 | * Examples
33 | * ```
34 | * $x = some(2);
35 | * assert($x->isSome(), 'Expected $x to be Some.');
36 | *
37 | * $x = none();
38 | * assert(!$x->isSome(), 'Expected $x not to be Some.');
39 | * ```
40 | *
41 | * @return bool `true` if the option is a {@see Some} value, otherwise `false`
42 | */
43 | public function isSome(): bool;
44 |
45 | /**
46 | * Returns `true` if the option is {@see None} value.
47 | *
48 | * Examples
49 | * ```
50 | * $x = some(2);
51 | * assert(!$x->isNone(), 'Expected $x not to be None.');
52 | *
53 | * $x = none();
54 | * assert($x->isNone(), 'Expected $x to be None.');
55 | * ```
56 | *
57 | * @return bool `true` if the option is {@see None} value, otherwise `false`
58 | */
59 | public function isNone(): bool;
60 |
61 | /**
62 | * Matches the option with the provided callables and returns the result.
63 | *
64 | * @template U
65 | * @template V
66 | *
67 | * @param callable(T): U $some A callable that returns a value of type U
68 | * @param callable(): V $none A callable that returns a value of type V
69 | *
70 | * @return U|V The result of the callable `$some` function if the option is {@see Some},
71 | * otherwise the result of the callable `$none` function
72 | */
73 | public function match(callable $some, callable $none): mixed;
74 |
75 | /**
76 | * Returns the contained {@see Some} value, or throws an exception with custom message if the value is {@see None}.
77 | *
78 | * Examples
79 | * ```
80 | * $x = some(2);
81 | * assert(2 === $x->expect('A number must be provided.'), 'Expected $x to be 2.');
82 | *
83 | * $x = none();
84 | * $x->expect('A number.'); // throws RuntimeOptionException
85 | * ```
86 | *
87 | * @param string $message A custom error message to use in the RuntimeOptionException
88 | *
89 | * @return T The contained value
90 | *
91 | * @throws RuntimeOptionException If the value is {@see None} with a custom error message provided.
92 | * We recommend that `expect()` messages are used to describe the reason
93 | * you expect the `Option` should be {@see Some}.
94 | */
95 | public function expect(string $message): mixed;
96 |
97 | /**
98 | * Returns the contained {@see Some} value, or throws an exception if the value is {@see None}.
99 | *
100 | * Examples
101 | * ```
102 | * $x = some(2);
103 | * assert(2 === $x->unwrap(), 'Expected $x to be 2.');
104 | *
105 | * $x = none();
106 | * $x->unwrap(); // throws LogicOptionException
107 | * ```
108 | *
109 | * @return T The contained value
110 | *
111 | * @throws RuntimeOptionException If the value is {@see None}
112 | */
113 | public function unwrap(): mixed;
114 |
115 | /**
116 | * Returns the contained {@see Some} value or a provided default.
117 | *
118 | * Examples
119 | * ```
120 | * $x = some(2);
121 | * assert(2 === $x->unwrapOr(1), 'Expected $x to be 2.');
122 | *
123 | * $x = none();
124 | * assert(1 === $x->unwrapOr(1), 'Expected $x to be 1.');
125 | * ```
126 | *
127 | * @template U of T
128 | *
129 | * @param U $default A default value of type T
130 | *
131 | * @return T|U The contained value or the default
132 | */
133 | public function unwrapOr(mixed $default): mixed;
134 |
135 | /**
136 | * Returns the contained {@see Some} value or computes it from a closure.
137 | *
138 | * Examples
139 | * ```
140 | * $x = some(2);
141 | * assert(2 === $x->unwrapOrElse(fn () => 1), 'Expected $x to be 2.');
142 | *
143 | * $x = none();
144 | * assert(1 === $x->unwrapOrElse(fn () => 1), 'Expected $x to be 1.');
145 | * ```
146 | *
147 | * @template U
148 | *
149 | * @param callable(): U $fn A callable that returns a value of type U
150 | *
151 | * @return T|U The contained value or the result of the callable
152 | */
153 | public function unwrapOrElse(callable $fn): mixed;
154 |
155 | /**
156 | * Returns the contained {@see Some} value or throws an exception.
157 | *
158 | * Examples
159 | * ```
160 | * $x = some(2);
161 | * assert(2 === $x->unwrapOrThrow(), 'Expected $x to be 2.');
162 | *
163 | * $x = none();
164 | * $x->unwrapOrThrow(new UnknownNumberError()); // throws UnknownNumberError
165 | * ```
166 | *
167 | * @return T The contained value
168 | *
169 | * @throws \Throwable If the value is {@see None}
170 | */
171 | public function unwrapOrThrow(\Throwable $error): mixed;
172 |
173 | /**
174 | * Maps an `Option` to `Option` by applying a function to a contained value (if `Some`)
175 | * or returns `None` (if `None`).
176 | *
177 | * @template U
178 | *
179 | * @param callable(T): U $fn A callable that returns a value of type U
180 | *
181 | * @return self|self The mapped Option
182 | */
183 | public function map(callable $fn): self;
184 |
185 | /**
186 | * Returns the provided default result (if `None`),
187 | * or applies a function to the contained value (if `Some`).
188 | *
189 | * Examples
190 | * ```
191 | * $x = some(2);
192 | * assert(4 === $x->mapOr(fn ($value) => $value * 2, 0), 'Expected $x to be 4.');
193 | *
194 | * $x = none();
195 | * assert(0 === $x->mapOr(fn ($value) => $value * 2, 0), 'Expected $x to be 0.');
196 | * ```
197 | *
198 | * @template U
199 | *
200 | * @param callable(T): U $fn A callable that returns a value of type U
201 | * @param U $default A default value of type U
202 | *
203 | * @return T|U The contained value or the default
204 | */
205 | public function mapOr(callable $fn, mixed $default): mixed;
206 |
207 | /**
208 | * Computes a default function result (if `None`), or
209 | * applies a different function to the contained value (if any).
210 | *
211 | * Examples
212 | * ```
213 | * $x = some(2);
214 | * assert(4 === $x->mapOrElse(fn ($value) => $value * 2, fn () => 0), 'Expected $x to be 4.');
215 | *
216 | * $x = none();
217 | * assert(0 === $x->mapOrElse(fn ($value) => $value * 2, fn () => 0), 'Expected $x to be 0.');
218 | * ```
219 | *
220 | * @template U
221 | *
222 | * @param callable(T): U $fn A callable that returns a value of type U
223 | * @param callable(): U $default A callable that returns a default value of type U
224 | *
225 | * @return U The result of the callable
226 | */
227 | public function mapOrElse(callable $fn, callable $default): mixed;
228 |
229 | /**
230 | * Returns the option if it contains a value, otherwise returns `$option`.
231 | *
232 | * Examples
233 | * ```
234 | * $x = some(2);
235 | * $y = some(3);
236 | * assert($x->or($y)->isSome(), 'Expected $x to be Some.');
237 | *
238 | * $x = none();
239 | * $y = some(3);
240 | * assert($x->or($y)->isSome(), 'Expected $x to be Some.');
241 | *
242 | * $x = none();
243 | * $y = none();
244 | * assert($x->or($y)->isNone(), 'Expected $x to be None.');
245 | * ```
246 | *
247 | * @param self $option The option to return if the original is {@see None}
248 | *
249 | * @return self The original option if it contains a value, otherwise `$option`
250 | */
251 | public function or(self $option): self;
252 |
253 | /**
254 | * Returns the option if it contains a value, otherwise calls `$fn` and
255 | * returns the result.
256 | *
257 | * Examples
258 | * ```
259 | * $x = some(2);
260 | * $y = some(3);
261 | * assert($x->orElse(fn () => $y)->isSome(), 'Expected $x to be Some.');
262 | *
263 | * $x = none();
264 | * $y = some(3);
265 | * assert($x->orElse(fn () => $y)->isSome(), 'Expected $x to be Some.');
266 | *
267 | * $x = none();
268 | * $y = none();
269 | * assert($x->orElse(fn () => $y)->isNone(), 'Expected $x to be None.');
270 | * ```
271 | *
272 | * @param callable(): self $fn A callable that returns an Option
273 | *
274 | * @return self The original option if it contains a value, otherwise the result of the callable
275 | */
276 | public function orElse(callable $fn): self;
277 |
278 | /**
279 | * Returns {@see Some} if exactly one of `$this`, `$option` is {@see Some}, otherwise returns {@see None}.
280 | *
281 | * Examples
282 | * ```
283 | * $x = some(2);
284 | * $y = some(3);
285 | * assert($x->xor($y)->isNone(), 'Expected $x to be None.');
286 | *
287 | * $x = none();
288 | * $y = some(3);
289 | * assert($x->xor($y)->isSome(), 'Expected $x to be Some.');
290 | *
291 | * $x = none();
292 | * $y = none();
293 | * assert($x->xor($y)->isNone(), 'Expected $x to be None.');
294 | * ```
295 | *
296 | * @param self $option The option to compare with
297 | *
298 | * @return self|self
299 | */
300 | public function xor(self $option): self;
301 |
302 | /**
303 | * Returns {@see None} if the option is {@see None}, otherwise returns `$option`.
304 | *
305 | * Examples
306 | * ```
307 | * $x = some(2);
308 | * $y = some(3);
309 | * assert($x->and($y)->isSome(), 'Expected $x and $y to be Some.');
310 | *
311 | * $x = none();
312 | * $y = some(3);
313 | * assert($x->and($y)->isNone(), 'Expected $x to be None.');
314 | *
315 | * $x = none();
316 | * $y = none();
317 | * assert($x->and($y)->isNone(), 'Expected $x and $y to be None.');
318 | * ```
319 | *
320 | * @param self $option The option to compare with
321 | *
322 | * @return self|self `$option` if the original is {@see None}, otherwise the original option
323 | */
324 | public function and(self $option): self;
325 |
326 | /**
327 | * Returns {@see None} if the Option is {@see None}, otherwise calls `$fn` with
328 | * the wrapped value and returns the result.
329 | *
330 | * Some languages call this method flatmap.
331 | *
332 | * Examples
333 | * ```
334 | * $x = some(2)->andThen(fn ($value) => some($value * 2));
335 | * assert(4 === $x->unwrap(), 'Expected $x to be 4.');
336 | *
337 | * $x = none()->andThen(fn ($value) => some($value * 2));
338 | * assert($x->isNone(), 'Expected $x to be None.');
339 | * ```
340 | *
341 | * @template U
342 | *
343 | * @param callable(T): self $fn A callable that returns an Option
344 | *
345 | * @return self|self The result of the callable
346 | */
347 | public function andThen(callable $fn): self;
348 |
349 | /**
350 | * Returns an iterator over the possibly contained value.
351 | *
352 | * Examples
353 | * ```
354 | * $x = some(2);
355 | * assert([2] === iterator_to_array($x->iterate()), 'Expected to be [2].');
356 | *
357 | * $x = none();
358 | * assert([] === iterator_to_array($x->iterate()), 'Expected to be [].');
359 | * ```
360 | *
361 | * @return iterable An iterator over the possibly contained value
362 | */
363 | public function iterate(): iterable;
364 |
365 | /**
366 | * Returns {@see None} if the Option is {@see None}, otherwise calls `$predicate`
367 | * with the wrapped value and returns:
368 | *
369 | * - {@see Some}(v) If `$predicate` returns `true` (where `v` is the wrapped value), and
370 | * - {@see None} if `$predicate` returns `false`.
371 | *
372 | * Examples
373 | * ```
374 | * $isEven = fn (int $value): bool => 0 === $value % 2;
375 | *
376 | * assert(none()->filter($isEven)->isNone(), 'Expected to be true.');
377 | * assert(some(3)->filter($isEven)->isNone(), 'Expected to be true.');
378 | * assert(some(2)->filter($isEven)->isSome(), 'Expected to be true.');
379 | * ```
380 | *
381 | * @param callable(T): bool $predicate A callable that returns a boolean
382 | *
383 | * @return self {@see Some} if it's {@see Some} option and the predicate is `true`, otherwise {@see None}
384 | */
385 | public function filter(callable $predicate): self;
386 |
387 | /**
388 | * Compares the option with the provided option and returns `true` if they are equal.
389 | *
390 | * Examples
391 | * ```
392 | * $x = some(2);
393 | * $y = some(2);
394 | * assert($x->equals($y), 'Expected $x to be equal to $y.');
395 | *
396 | * $x = some(2);
397 | * $y = some(3);
398 | * assert(!$x->equals($y), 'Expected $x not to be equal to $y.');
399 | *
400 | * $x = none();
401 | * $y = none();
402 | * assert($x->equals($y), 'Expected $x to be equal to $y.');
403 | * ```
404 | *
405 | * @param self $option The option to compare with
406 | *
407 | * @return bool `true` if the wrapped value is the same value as `$option`, otherwise `false`
408 | */
409 | public function equals(self $option): bool;
410 |
411 | /**
412 | * Converts from `Option