Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions src/Psl/Option/Option.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@ public function unwrap(): mixed
* @note: Arguments passed to `Option::unwrapOr()` are eagerly evaluated;
* if you are passing the result of a function call, it is recommended to use `Option::unwrapOrElse()`, which is lazily evaluated.
*
* @param T $default
* @template O
*
* @return T
* @param O $default
*
* @return T|O
*/
public function unwrapOr(mixed $default): mixed
{
Expand All @@ -114,9 +116,11 @@ public function unwrapOr(mixed $default): mixed
/**
* Returns the contained some value or computes it from a closure.
*
* @param (Closure(): T) $default
* @template O
*
* @return T
* @param (Closure(): O) $default
*
* @return T|O
*/
public function unwrapOrElse(Closure $default): mixed
{
Expand Down Expand Up @@ -215,6 +219,25 @@ public function map(Closure $closure): Option
return $this;
}

/**
* Maps an `Option<T>` to `Option<Tu>` by applying a function to a contained value that returns an Option<Tu>.
*
* @template Tu
*
* @param (Closure(T): Option<Tu>) $closure
*
* @return Option<Tu>
*/
public function andThen(Closure $closure): Option
{
if ($this->option !== null) {
return $closure($this->option[0]);
}

/** @var Option<Tu> */
return $this;
}

/**
* Applies a function to the contained value (if some),
* or returns `Option<Tu>::some()` with the provided `$default` value (if none).
Expand Down
25 changes: 25 additions & 0 deletions tests/static-analysis/Option/unwrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

use Psl\Option;

function test_some_unwrap_or(): ?string
{
return Option\some('string')->unwrapOr(null);
}

function test_none_unwrap_or(): ?string
{
return Option\none()->unwrapOr(null);
}

function test_some_unwrap_or_else(): ?string
{
return Option\some('string')->unwrapOrElse(static fn () => null);
}

function test_none_unwrap_or_else(): ?string
{
return Option\none()->unwrapOrElse(static fn() => null);
}
7 changes: 7 additions & 0 deletions tests/unit/Option/NoneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,11 @@ public function testMapOrElse(): void

static::assertSame(4, $option->mapOrElse(static fn($i) => $i + 1, static fn() => 4)->unwrap());
}

public function testAndThen(): void
{
$option = Option\none();

static::assertNull($option->andThen(static fn($i) => Option\some($i + 1))->unwrapOr(null));
}
}
7 changes: 7 additions & 0 deletions tests/unit/Option/SomeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,11 @@ public function testMapOrElse(): void

static::assertSame(3, $option->mapOrElse(static fn($i) => $i + 1, static fn() => 4)->unwrap());
}

public function testAndThen(): void
{
$option = Option\some(2);

static::assertSame(3, $option->andThen(static fn($i) => Option\some($i + 1))->unwrapOr(null));
}
}