Add toPhpOption; convert both to mostly non-static
This commit is contained in:
265
src/Option.php
265
src/Option.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace BitBadger\InspiredByFSharp;
|
||||
|
||||
use Error;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
@@ -25,7 +26,7 @@ use InvalidArgumentException;
|
||||
*/
|
||||
readonly class Option
|
||||
{
|
||||
/** @var ?T $value The value for this option */
|
||||
/** @var T|null $value The value for this option */
|
||||
private mixed $value;
|
||||
|
||||
/**
|
||||
@@ -49,6 +50,138 @@ readonly class Option
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this option have a `None` value?
|
||||
*
|
||||
* @return bool True if the option is `None`, false if it is `Some`
|
||||
*/
|
||||
public function isNone(): bool
|
||||
{
|
||||
return is_null($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this option have a `Some` value?
|
||||
*
|
||||
* @return bool True if the option is `Some`, false if it is `None`
|
||||
*/
|
||||
public function isSome(): bool
|
||||
{
|
||||
return !$this->isNone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value, or a default value, from an option
|
||||
*
|
||||
* @param T $default The default value to return if the option is `None`
|
||||
* @return T The `Some` value, or the default value if the option is `None`
|
||||
*/
|
||||
public function getOrDefault(mixed $default): mixed
|
||||
{
|
||||
return $this->value ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value, or return the value of a callable function
|
||||
*
|
||||
* @template U The return type of the callable provided
|
||||
* @param callable(): U $f The callable function to use for `None` options
|
||||
* @return T|mixed The value if `Some`, the result of the callable if `None`
|
||||
*/
|
||||
public function getOrCall(callable $f): mixed
|
||||
{
|
||||
return $this->value ?? $f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map this optional value to another value
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(T): U $f The mapping function
|
||||
* @return Option<U> A `Some` instance with the transformed value if `Some`, `None` otherwise
|
||||
*/
|
||||
public function map(callable $f): self
|
||||
{
|
||||
return $this->isSome() ? self::Some($f($this->get())) : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function on the value (if it exists)
|
||||
*
|
||||
* @param callable(T): void $f The function to call
|
||||
*/
|
||||
public function iter(callable $f): void
|
||||
{
|
||||
if ($this->isSome()) {
|
||||
$f($this->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an option into `None` if it does not match the given function
|
||||
*
|
||||
* @param callable(T): bool $f The filter function to run
|
||||
* @return Option<T> The option, if it was `Some` and the function returned true; `None` otherwise
|
||||
*/
|
||||
public function filter(callable $f): self
|
||||
{
|
||||
return match (true) {
|
||||
$this->isNone() => $this,
|
||||
default => $f($this->value) ? $this : self::None(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the option have the given value?
|
||||
*
|
||||
* @param T $value The value to be checked
|
||||
* @param bool $strict True for strict equality (`===`), false for loose equality (`==`)
|
||||
* @return bool True if the value matches, false if not; `None` always returns false
|
||||
*/
|
||||
public function is(mixed $value, bool $strict = true): bool
|
||||
{
|
||||
return match (true) {
|
||||
$this->isNone() => false,
|
||||
default => $strict ? $this->value === $value : $this->value == $value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely retrieve the optional value as a nullable value
|
||||
*
|
||||
* @return T|null The value for `Some` instances, `null` for `None` instances
|
||||
*/
|
||||
public function unwrap(): mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tap into the `Result` for a secondary action, returning the result
|
||||
*
|
||||
* @param callable(Option<T>): mixed $f The function to run (return value is ignored)
|
||||
* @return Option<T> The same option provided
|
||||
*/
|
||||
public function tap(callable $f): Option
|
||||
{
|
||||
$f($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to a PhpOption option
|
||||
*
|
||||
* @return mixed An option from the PhpOption library
|
||||
*/
|
||||
public function toPhpOption(): mixed
|
||||
{
|
||||
return match (true) {
|
||||
$this->isNone() && class_exists('PhpOption\None') => call_user_func('PhpOption\None::create'),
|
||||
class_exists('PhpOption\Some') => call_user_func('PhpOption\Some::create', $this->value),
|
||||
default => throw new Error('PhpOption types could not be found'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `Some` option with the given value
|
||||
*
|
||||
@@ -83,136 +216,8 @@ readonly class Option
|
||||
{
|
||||
return match (true) {
|
||||
is_object($value) && is_a($value, 'PhpOption\Option') =>
|
||||
$value->isDefined() ? self::Some($value->get()) : self::None(),
|
||||
$value->isDefined() ? self::Some($value->get()) : self::None(),
|
||||
default => new self($value),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this option have a `None` value?
|
||||
*
|
||||
* @param Option<T> $it The option in question
|
||||
* @return bool True if the option is `None`, false if it is `Some`
|
||||
*/
|
||||
public static function isNone(Option $it): bool
|
||||
{
|
||||
return is_null($it->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this option have a `Some` value?
|
||||
*
|
||||
* @param Option<T> $it The option in question
|
||||
* @return bool True if the option is `Some`, false if it is `None`
|
||||
*/
|
||||
public static function isSome(Option $it): bool
|
||||
{
|
||||
return !self::isNone($it);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value, or a default value, from an option
|
||||
*
|
||||
* @param T $default The default value to return if the option is `None`
|
||||
* @param Option<T> $it The option in question
|
||||
* @return T The `Some` value, or the default value if the option is `None`
|
||||
*/
|
||||
public static function defaultValue(mixed $default, Option $it): mixed
|
||||
{
|
||||
return self::isSome($it) ? $it->get() : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value, or return the value of a callable function
|
||||
*
|
||||
* @template U The return type of the callable provided
|
||||
* @param callable(): U $f The callable function to use for `None` options
|
||||
* @param Option<T> $it The option in question
|
||||
* @return T|mixed The value if `Some`, the result of the callable if `None`
|
||||
*/
|
||||
public static function getOrCall(callable $f, Option $it): mixed
|
||||
{
|
||||
return self::isSome($it) ? $it->get() : $f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map this optional value to another value
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(T): U $f The mapping function
|
||||
* @param Option<T> $it The option in question
|
||||
* @return Option<U> A `Some` instance with the transformed value if `Some`, `None` otherwise
|
||||
*/
|
||||
public static function map(callable $f, Option $it): self
|
||||
{
|
||||
return self::isSome($it) ? self::Some($f($it->get())) : self::None();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function on the value (if it exists)
|
||||
*
|
||||
* @param callable(T): void $f The function to call
|
||||
* @param Option<T> $it The option in question
|
||||
*/
|
||||
public static function iter(callable $f, Option $it): void
|
||||
{
|
||||
if (self::isSome($it)) {
|
||||
$f($it->get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform an option into `None` if it does not match the given function
|
||||
*
|
||||
* @param callable(T): bool $f The filter function to run
|
||||
* @param Option<T> $it The option in question
|
||||
* @return Option<T> The option, if it was `Some` and the function returned true; `None` otherwise
|
||||
*/
|
||||
public static function filter(callable $f, Option $it): self
|
||||
{
|
||||
return match (true) {
|
||||
self::isNone($it) => self::None(),
|
||||
default => $f($it->get()) ? self::Some($it->get()) : self::None(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the option have the given value?
|
||||
*
|
||||
* @param T $value The value to be checked
|
||||
* @param Option<T> $it The option in question
|
||||
* @param bool $strict True for strict equality (`===`), false for loose equality (`==`)
|
||||
* @return bool True if the value matches, false if not; `None` always returns false
|
||||
*/
|
||||
public static function is(mixed $value, Option $it, bool $strict = true): bool
|
||||
{
|
||||
return match (true) {
|
||||
self::isNone($it) => false,
|
||||
default => $strict ? $it->value === $value : $it->value == $value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely retrieve the optional value as a nullable value
|
||||
*
|
||||
* @param Option<T> $it The option in question
|
||||
* @return ?T The value for `Some` instances, `null` for `None` instances
|
||||
*/
|
||||
public static function unwrap(Option $it): mixed
|
||||
{
|
||||
return self::isSome($it) ? $it->get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tap into the `Result` for a secondary action, returning the result
|
||||
*
|
||||
* @param callable(Option<T>): mixed $f The function to run (return value is ignored)
|
||||
* @param Option<T> $it The option against which the function should be run
|
||||
* @return Option<T> The same option provided
|
||||
*/
|
||||
public static function tap(callable $f, Option $it): Option
|
||||
{
|
||||
$f($it);
|
||||
return $it;
|
||||
}
|
||||
}
|
||||
|
||||
163
src/Result.php
163
src/Result.php
@@ -66,6 +66,84 @@ readonly class Result
|
||||
return $this->errorValue->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this result `OK`?
|
||||
*
|
||||
* @return bool True if the result is `OK`, false if it is `Error`
|
||||
*/
|
||||
public function isOK(): bool
|
||||
{
|
||||
return $this->okValue->isSome();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this result `Error`?
|
||||
*
|
||||
* @return bool True if the result is `Error`, false if it is `OK`
|
||||
*/
|
||||
public function isError(): bool
|
||||
{
|
||||
return $this->errorValue->isSome();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an `OK` result to another, leaving an `Error` result unmodified
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(TOK): U $f The mapping function
|
||||
* @return Result<U, TError> A transformed `OK` instance or the original `Error` instance
|
||||
*/
|
||||
public function map(callable $f): self
|
||||
{
|
||||
return $this->isOK() ? self::OK($f($this->getOK())) : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an `Error` result to another, leaving an `OK` result unmodified
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(TError): U $f The mapping function
|
||||
* @return Result<TOK, U> A transformed `Error` instance or the original `OK` instance
|
||||
*/
|
||||
public function mapError(callable $f): self
|
||||
{
|
||||
return $this->isError() ? self::Error($f($this->getError())) : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function on an `OK` value (if it exists)
|
||||
*
|
||||
* @param callable(TOK): void $f The function to call
|
||||
*/
|
||||
public function iter(callable $f): void
|
||||
{
|
||||
if ($this->isOK()) {
|
||||
$f($this->getOK());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a `Result`'s `OK` value to an `Option`
|
||||
*
|
||||
* @return Option<TOK> A `Some` option with the OK value if `OK`, `None` if `Error`
|
||||
*/
|
||||
public function toOption(): Option
|
||||
{
|
||||
return $this->isOK() ? Option::Some($this->getOK()) : Option::None();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tap into the `Result` for a secondary action, returning the result
|
||||
*
|
||||
* @param callable(Result<TOK, TError>): mixed $f The function to run (return value is ignored)
|
||||
* @return Result<TOK, TError> The same result provided
|
||||
*/
|
||||
public function tap(callable $f): Result
|
||||
{
|
||||
$f($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an `OK` result
|
||||
*
|
||||
@@ -93,89 +171,4 @@ readonly class Result
|
||||
}
|
||||
return new self(errorValue: $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given result `OK`?
|
||||
*
|
||||
* @param Result $it The result in question
|
||||
* @return bool True if the result is `OK`, false if it is `Error`
|
||||
*/
|
||||
public static function isOK(Result $it): bool
|
||||
{
|
||||
return Option::isSome($it->okValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given result `Error`?
|
||||
*
|
||||
* @param Result $it The result in question
|
||||
* @return bool True if the result is `Error`, false if it is `OK`
|
||||
*/
|
||||
public static function isError(Result $it): bool
|
||||
{
|
||||
return Option::isSome($it->errorValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an `OK` result to another, leaving an `Error` result unmodified
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(TOK): U $f The mapping function
|
||||
* @param Result<TOK, TError> $it The result in question
|
||||
* @return Result<U, TError> A transformed `OK` instance, or an `Error` instance with the same value
|
||||
*/
|
||||
public static function map(callable $f, Result $it): self
|
||||
{
|
||||
return self::isOK($it) ? self::OK($f($it->getOK())) : self::Error($it->getError());
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an `Error` result to another, leaving an `OK` result unmodified
|
||||
*
|
||||
* @template U The type of the mapping function
|
||||
* @param callable(TError): U $f The mapping function
|
||||
* @param Result<TOK, TError> $it The result in question
|
||||
* @return Result<TOK, U> A transformed `Error` instance, or an `OK` instance with the same value
|
||||
*/
|
||||
public static function mapError(callable $f, Result $it): self
|
||||
{
|
||||
return self::isError($it) ? self::Error($f($it->getError())) : self::OK($it->getOK());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function on an `OK` value (if it exists)
|
||||
*
|
||||
* @param callable(TOK): void $f The function to call
|
||||
* @param Result<TOK, TError> $it The result in question
|
||||
*/
|
||||
public static function iter(callable $f, Result $it): void
|
||||
{
|
||||
if (self::isOK($it)) {
|
||||
$f($it->getOK());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a `Result`'s `OK` value to an `Option`
|
||||
*
|
||||
* @param Result<TOK, TError> $it The result in question
|
||||
* @return Option<TOK> A `Some` option with the OK value if `OK`, `None` if `Error`
|
||||
*/
|
||||
public static function toOption(Result $it): Option
|
||||
{
|
||||
return Result::isOK($it) ? Option::Some($it->getOK()) : Option::None();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tap into the `Result` for a secondary action, returning the result
|
||||
*
|
||||
* @param callable(Result<TOK, TError>): mixed $f The function to run (return value is ignored)
|
||||
* @param Result<TOK, TError> $it The result against which the function should be run
|
||||
* @return Result<TOK, TError> The same result provided
|
||||
*/
|
||||
public static function tap(callable $f, Result $it): Result
|
||||
{
|
||||
$f($it);
|
||||
return $it;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user