diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bd170e9..0cff401 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,13 +14,17 @@ "XDEBUG_SESSION": "1", "XDEBUG_CONFIG": "log_level=0", "PATH": "${containerEnv:PATH}:/app/vendor/bin", - "PROMPT_COMMAND": "short_pwd=$(sed \"s:\\([^/\\.]\\)[^/]*/:\\1/:g\" <<< ${PWD/#$HOME/\\~})", + "PROMPT_COMMAND": "short_pwd=$(sed \"s:\\([^/\\.]\\)[^/]*/:\\1/:g\" <<< ${PWD/#$HOME/\\~})", "PS1": "\\[\\e[0;35m\\]$short_pwd \\n\\[\\e[0;33m\\]$((( _ret_value )) && printf \"\\[\\e[0;31m\\]\")$\\[\\e[0m\\] ", "PS2": "\\[${_y}\\]❯ \\[${reset}\\]" }, // Set *default* container specific settings.json values on container create. "settings": {}, + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/sshd:1": {} + }, // Add the IDs of extensions you want installed when the container is created. // Note that some extensions may not work in Alpine Linux. See https://aka.ms/vscode-remote/linux. diff --git a/src/Option.php b/src/Option.php index 064c516..a4703d1 100644 --- a/src/Option.php +++ b/src/Option.php @@ -503,6 +503,33 @@ public function mapOrElse(callable $callback, callable $default): mixed; */ public function zip(Option $option): Option; + /** + * Zips `$this` and another `Option` with a function. + * + * If `$this` is `Some(s)` and other is `Some(o)`, this method returns `Some(f([s, o]))`. + * Otherwise, `None` is returned. + * + * # Examples + * + * ``` + * $x = Option\some(new \DateTime('2025-02-08T23:15:07+00:00')); + * $y = Option\some(new \DateInterval('P1M0D')); + * // @var Option $z + * $z = Option\none(); + * self::assertEq($x->zipWith($y, date_add(...)), Option\some(new \DateTimeImmutable('2025-03-08T23:15:07+00:00'))); + * self::assertSame($x->zipWith($z, date_add(...)), Option\none()); + * self::assertSame($z->zipWith($y, date_add(...)), Option\none()); + * ``` + * + * @template U + * @template V + * @param Option $option + * @param callable(T, U):V $callback + * @return (T is never ? Option\None : (U is never ? Option\None : Option)) + * @phpstan-return Option + */ + public function zipWith(Option $option, callable $callback): Option; + /** * Transforms the `Option` into a `Result`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. * diff --git a/src/Option/None.php b/src/Option/None.php index ec7fa48..a1230c2 100644 --- a/src/Option/None.php +++ b/src/Option/None.php @@ -161,6 +161,18 @@ public function zip(Option $option): self return $this; } + /** + * @template U + * @template V + * @param Option $option + * @param callable(never, U):V $callback + * @return $this + */ + public function zipWith(Option $option, callable $callback): self + { + return $this; + } + /** * @template E * @param E $err diff --git a/src/Option/Some.php b/src/Option/Some.php index 37dab44..b5bcf5d 100644 --- a/src/Option/Some.php +++ b/src/Option/Some.php @@ -192,6 +192,23 @@ public function zip(Option $option): Option return Option\none(); } + /** + * @template U + * @template V + * @param Option $option + * @param callable(T, U):V $callback + * @return (U is never ? Option\None : Option) + * @return Option + */ + public function zipWith(Option $option, callable $callback): Option + { + foreach ($option as $value) { + return Option\some($callback($this->value, $value)); + } + + return Option\none(); + } + /** * @template E * @param E $err