3 Commits
v1 ... v2.0.0

Author SHA1 Message Date
c61ff7a831 Convert tests to Pest 2024-11-20 22:01:40 -05:00
9327d8fa29 Update properties in README 2024-09-30 23:06:57 -04:00
483d7875d5 Change functions to properties
- Force PHP 8.4
2024-09-30 22:59:46 -04:00
7 changed files with 116 additions and 147 deletions

View File

@@ -2,6 +2,8 @@
This project contains PHP utility classes whose functionality is inspired by their F# counterparts. This project contains PHP utility classes whose functionality is inspired by their F# counterparts.
The v2 series requires at least PHP 8.4. A similar API exists for PHP 8.2 - 8.3 in version 1 of this project; see its README for specifics.
## What It Provides ## What It Provides
This early-stage library currently provides two classes, both of which are designed to wrap values and indicate the state of the action that produced them. `Option<T>` represents a variable that may or may not have a value. `Result<TOK, TError>` represents the result of an action; the "ok" and "error" states both provide a value. This early-stage library currently provides two classes, both of which are designed to wrap values and indicate the state of the action that produced them. `Option<T>` represents a variable that may or may not have a value. `Result<TOK, TError>` represents the result of an action; the "ok" and "error" states both provide a value.
@@ -11,12 +13,12 @@ This early-stage library currently provides two classes, both of which are desig
| **Creating** | `::Some(T)` for Some | `::OK(TOK)` for OK | | **Creating** | `::Some(T)` for Some | `::OK(TOK)` for OK |
| | `::None()` for None | `::Error(TError)` for Error | | | `::None()` for None | `::Error(TError)` for Error |
| | `::of($value)` _None if `null`_ | | | | `::of($value)` _None if `null`_ | |
| **Querying** | `->isSome(): bool` | `->isOK(): bool` | | **Querying** | `->isSome: bool` | `->isOK: bool` |
| | `->isNone(): bool` | `->isError(): bool` | | | `->isNone: bool` | `->isError: bool` |
| | `->contains(T, $strict = true): bool` | `->contains(TOK, $strict = true): bool` | | | `->contains(T, $strict = true): bool` | `->contains(TOK, $strict = true): bool` |
| | `->exists(callable(T): bool): bool` | `->exists(callable(TOK): bool): bool` | | | `->exists(callable(T): bool): bool` | `->exists(callable(TOK): bool): bool` |
| **Reading**<br> | `->get(): T` | `->getOK(): TOK` | | **Reading**<br> | `->value: T` | `->ok: TOK` |
| _all throw if called on missing value_ | | `->getError(): TError` | | _all throw if called on missing value_ | | `->error: TError` |
| **Transforming**<br> | `->map(callable(T): TMapped): Option<TMapped>` | `->map(callable(TOK): TMapped): Result<TMapped, TError>` | | **Transforming**<br> | `->map(callable(T): TMapped): Option<TMapped>` | `->map(callable(TOK): TMapped): Result<TMapped, TError>` |
| _all still `Option` or `Result`_ | | `->mapError(callable(TError): TMapped): Result<TOK, TMapped>` | | _all still `Option` or `Result`_ | | `->mapError(callable(TError): TMapped): Result<TOK, TMapped>` |
| **Iterating** | `->iter(callable(T): void): void` | `->iter(callable(TOK): void): void` | | **Iterating** | `->iter(callable(T): void): void` | `->iter(callable(TOK): void): void` |

View File

@@ -17,7 +17,7 @@
"rss": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp.rss" "rss": "https://git.bitbadger.solutions/bit-badger/inspired-by-fsharp.rss"
}, },
"require": { "require": {
"php": "8.2 - 8.3" "php": ">=8.4"
}, },
"require-dev": { "require-dev": {
"phpoption/phpoption": "^1", "phpoption/phpoption": "^1",

4
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "87ff0fadc5082e5dfa570a11147687dd", "content-hash": "993898f46125d481cf00405cbe9c4069",
"packages": [], "packages": [],
"packages-dev": [ "packages-dev": [
{ {
@@ -3826,7 +3826,7 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "8.2 - 8.3" "php": ">=8.4"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"

View File

@@ -25,50 +25,38 @@ use InvalidArgumentException;
* *
* @template T The type of value represented by this option * @template T The type of value represented by this option
*/ */
readonly class Option class Option
{ {
/** @var T|null $value The value for this option */ /** @var T|null $value The value for this option */
private mixed $value; private mixed $val;
/** /**
* @param T|null $value The possibly null value for this option * @param T|null $value The possibly null value for this option
*/ */
private function __construct(mixed $value = null) private function __construct(mixed $value = null)
{ {
$this->value = $value; $this->val = $value;
} }
/** /**
* Get the value of this option * @var T The value of this option (read-only)
* * @throws InvalidArgumentException If called on a `None` option
* @return T The value of the option
*/ */
public function get(): mixed public mixed $value {
{ get => match ($this->val) {
return match (true) { null => throw new InvalidArgumentException('Cannot get the value of a None option'),
$this->isSome() => $this->value, default => $this->val,
default => throw new InvalidArgumentException('Cannot get the value of a None option'),
}; };
} }
/** /** @var bool True if the option is `None`, false if it is `Some` */
* Does this option have a `None` value? public bool $isNone {
* get => is_null($this->val);
* @return bool True if the option is `None`, false if it is `Some`
*/
public function isNone(): bool
{
return is_null($this->value);
} }
/** /** @var bool True if the option is `Some`, false if it is `None` */
* Does this option have a `Some` value? public bool $isSome{
* get => !$this->isNone;
* @return bool True if the option is `Some`, false if it is `None`
*/
public function isSome(): bool
{
return !$this->isNone();
} }
/** /**
@@ -79,7 +67,7 @@ readonly class Option
*/ */
public function getOrDefault(mixed $default): mixed public function getOrDefault(mixed $default): mixed
{ {
return $this->value ?? $default; return $this->val ?? $default;
} }
/** /**
@@ -91,18 +79,19 @@ readonly class Option
*/ */
public function getOrCall(callable $f): mixed public function getOrCall(callable $f): mixed
{ {
return $this->value ?? $f(); return $this->val ?? $f();
} }
/** /**
* Get the value, or throw the * Get the value, or throw the exception using the given function
*
* @param callable(): Exception $exFunc A function to construct the exception to throw * @param callable(): Exception $exFunc A function to construct the exception to throw
* @return T The value of the option if `Some` * @return T The value of the option if `Some`
* @throws Exception If the option is `None` * @throws Exception If the option is `None`
*/ */
public function getOrThrow(callable $exFunc): mixed public function getOrThrow(callable $exFunc): mixed
{ {
return $this->value ?? throw $exFunc(); return $this->val ?? throw $exFunc();
} }
/** /**
@@ -117,7 +106,7 @@ readonly class Option
*/ */
public function bind(callable $f): Option public function bind(callable $f): Option
{ {
return $this->isNone() ? $this : $f($this->get()); return $this->isNone ? $this : $f($this->val);
} }
/** /**
@@ -130,8 +119,8 @@ readonly class Option
public function contains(mixed $value, bool $strict = true): bool public function contains(mixed $value, bool $strict = true): bool
{ {
return match (true) { return match (true) {
$this->isNone() => false, $this->isNone => false,
default => $strict ? $this->value === $value : $this->value == $value, default => $strict ? $this->val === $value : $this->val == $value,
}; };
} }
@@ -143,7 +132,7 @@ readonly class Option
*/ */
public function exists(callable $f): bool public function exists(callable $f): bool
{ {
return $this->isSome() ? $f($this->value) : false; return $this->isSome ? $f($this->val) : false;
} }
/** /**
@@ -155,7 +144,7 @@ readonly class Option
*/ */
public function map(callable $f): self public function map(callable $f): self
{ {
return $this->isSome() ? self::Some($f($this->get())) : $this; return $this->isSome ? self::Some($f($this->val)) : $this;
} }
/** /**
@@ -165,8 +154,8 @@ readonly class Option
*/ */
public function iter(callable $f): void public function iter(callable $f): void
{ {
if ($this->isSome()) { if ($this->isSome) {
$f($this->value); $f($this->val);
} }
} }
@@ -178,7 +167,7 @@ readonly class Option
*/ */
public function filter(callable $f): self public function filter(callable $f): self
{ {
return $this->isNone() || $this->exists($f) ? $this : self::None(); return $this->isNone || $this->exists($f) ? $this : self::None();
} }
/** /**
@@ -188,7 +177,7 @@ readonly class Option
*/ */
public function unwrap(): mixed public function unwrap(): mixed
{ {
return $this->value; return $this->val;
} }
/** /**
@@ -210,7 +199,7 @@ readonly class Option
*/ */
public function toArray(): array public function toArray(): array
{ {
return $this->isSome() ? [$this->value] : []; return $this->isSome ? [$this->val] : [];
} }
/** /**
@@ -221,8 +210,8 @@ readonly class Option
public function toPhpOption(): mixed public function toPhpOption(): mixed
{ {
return match (true) { return match (true) {
$this->isNone() && class_exists('PhpOption\None') => call_user_func('PhpOption\None::create'), $this->isNone && class_exists('PhpOption\None') => call_user_func('PhpOption\None::create'),
class_exists('PhpOption\Some') => call_user_func('PhpOption\Some::create', $this->value), class_exists('PhpOption\Some') => call_user_func('PhpOption\Some::create', $this->val),
default => throw new Error('PhpOption types could not be found'), default => throw new Error('PhpOption types could not be found'),
}; };
} }

View File

@@ -25,7 +25,7 @@ use InvalidArgumentException;
* @template TOK The type of the OK result * @template TOK The type of the OK result
* @template TError The type of the error result * @template TError The type of the error result
*/ */
readonly class Result class Result
{ {
/** @var Option<TOK> The OK value for this result */ /** @var Option<TOK> The OK value for this result */
private Option $okValue; private Option $okValue;
@@ -45,46 +45,24 @@ readonly class Result
$this->errorValue = Option::of($errorValue); $this->errorValue = Option::of($errorValue);
} }
/** /** @var TOK The OK value (will throw if result is not OK) */
* Get the value for an `OK` result public mixed $ok {
* get => $this->okValue->value;
* @return TOK The OK value for this result
* @throws InvalidArgumentException If the result is an `Error` result
*/
public function getOK(): mixed
{
return $this->okValue->get();
} }
/** /** @var TError The error value (will throw if result is not Error) */
* Get the value for an `Error` result public mixed $error {
* get => $this->errorValue->value;
* @return TError The error value for this result
* @throws InvalidArgumentException If the result is an `OK` result
*/
public function getError(): mixed
{
return $this->errorValue->get();
} }
/** /** @var bool True if the result is `OK`, false if it is `Error` */
* Is this result `OK`? public bool $isOK {
* get => $this->okValue->isSome;
* @return bool True if the result is `OK`, false if it is `Error`
*/
public function isOK(): bool
{
return $this->okValue->isSome();
} }
/** /** @var bool True if the result is `Error`, false if it is `OK` */
* Is this result `Error`? public bool $isError {
* get => $this->errorValue->isSome;
* @return bool True if the result is `Error`, false if it is `OK`
*/
public function isError(): bool
{
return $this->errorValue->isSome();
} }
/** /**
@@ -100,7 +78,7 @@ readonly class Result
*/ */
public function bind(callable $f): Result public function bind(callable $f): Result
{ {
return $this->isError() ? $this : $f($this->getOK()); return $this->isError ? $this : $f($this->ok);
} }
/** /**
@@ -113,7 +91,7 @@ readonly class Result
public function contains(mixed $value, bool $strict = true): bool public function contains(mixed $value, bool $strict = true): bool
{ {
return match (true) { return match (true) {
$this->isError() => false, $this->isError => false,
default => $this->okValue->contains($value, $strict), default => $this->okValue->contains($value, $strict),
}; };
} }
@@ -126,7 +104,7 @@ readonly class Result
*/ */
public function exists(callable $f): bool public function exists(callable $f): bool
{ {
return $this->isOK() ? $f($this->okValue->get()) : false; return $this->isOK ? $f($this->ok) : false;
} }
/** /**
@@ -138,7 +116,7 @@ readonly class Result
*/ */
public function map(callable $f): self public function map(callable $f): self
{ {
return $this->isOK() ? self::OK($f($this->getOK())) : $this; return $this->isOK ? self::OK($f($this->ok)) : $this;
} }
/** /**
@@ -150,7 +128,7 @@ readonly class Result
*/ */
public function mapError(callable $f): self public function mapError(callable $f): self
{ {
return $this->isError() ? self::Error($f($this->getError())) : $this; return $this->isError ? self::Error($f($this->error)) : $this;
} }
/** /**
@@ -160,8 +138,8 @@ readonly class Result
*/ */
public function iter(callable $f): void public function iter(callable $f): void
{ {
if ($this->isOK()) { if ($this->isOK) {
$f($this->getOK()); $f($this->ok);
} }
} }
@@ -182,7 +160,7 @@ readonly class Result
*/ */
public function toOption(): Option public function toOption(): Option
{ {
return $this->isOK() ? Option::Some($this->getOK()) : Option::None(); return $this->isOK ? Option::Some($this->ok) : Option::None();
} }
/** /**

View File

@@ -9,30 +9,30 @@ declare(strict_types=1);
use BitBadger\InspiredByFSharp\Option; use BitBadger\InspiredByFSharp\Option;
use PhpOption\{None, Some}; use PhpOption\{None, Some};
describe('->get()', function () { describe('->value', function () {
test('retrieves the value for Some', function () { test('retrieves the value for Some', function () {
expect(Option::Some(9)->get())->toBe(9); expect(Option::Some(9)->value)->toBe(9);
}); });
test('throws an exception for None', function () { test('throws an exception for None', function () {
expect(fn() => Option::None()->get())->toThrow(InvalidArgumentException::class); expect(fn() => Option::None()->value)->toThrow(InvalidArgumentException::class);
}); });
}); });
describe('->isNone()', function () { describe('->isNone', function () {
test('returns true for None', function () { test('returns true for None', function () {
expect(Option::None()->isNone())->toBeTrue(); expect(Option::None()->isNone)->toBeTrue();
}); });
test('returns false for Some', function () { test('returns false for Some', function () {
expect(Option::Some(8)->isNone())->toBeFalse(); expect(Option::Some(8)->isNone)->toBeFalse();
}); });
}); });
describe('->isSome()', function () { describe('->isSome', function () {
test('returns false for None', function () { test('returns false for None', function () {
expect(Option::None()->isSome())->toBeFalse(); expect(Option::None()->isSome)->toBeFalse();
}); });
test('returns true for Some', function () { test('returns true for Some', function () {
expect(Option::Some('boo')->isSome())->toBeTrue(); expect(Option::Some('boo')->isSome)->toBeTrue();
}); });
}); });
@@ -68,14 +68,14 @@ describe('->bind()', function () {
test('returns None when binding against None', function () { test('returns None when binding against None', function () {
$original = Option::None(); $original = Option::None();
$bound = $original->bind(fn($it) => Option::Some('value')); $bound = $original->bind(fn($it) => Option::Some('value'));
expect($bound)->isNone()->toBeTrue()->and($bound)->toBe($original); expect($bound)->isNone->toBeTrue()->and($bound)->toBe($original);
}); });
test('returns Some when binding against Some with Some', function () { test('returns Some when binding against Some with Some', function () {
expect(Option::Some('hello')->bind(fn($it) => Option::Some('goodbye'))) expect(Option::Some('hello')->bind(fn($it) => Option::Some('goodbye')))
->isSome()->toBeTrue()->get()->toBe('goodbye'); ->isSome->toBeTrue()->value->toBe('goodbye');
}); });
test('returns None when binding against Some with None', function () { test('returns None when binding against Some with None', function () {
expect(Option::Some('greetings')->bind(fn($it) => Option::None()))->isNone()->toBeTrue(); expect(Option::Some('greetings')->bind(fn($it) => Option::None()))->isNone->toBeTrue();
}); });
}); });
@@ -119,13 +119,13 @@ describe('->map()', function () {
return 'hello'; return 'hello';
}); });
expect($mapped) expect($mapped)
->isNone()->toBeTrue() ->isNone->toBeTrue()
->and($tattle->called)->toBeFalse() ->and($tattle->called)->toBeFalse()
->and($mapped)->toBe($none); ->and($mapped)->toBe($none);
}); });
test('maps value for Some', function () { test('maps value for Some', function () {
expect(Option::Some('abc ')->map(fn($it) => str_repeat($it, 2))) expect(Option::Some('abc ')->map(fn($it) => str_repeat($it, 2)))
->isSome()->toBeTrue()->get()->toBe('abc abc '); ->isSome->toBeTrue()->value->toBe('abc abc ');
}); });
test('throws an exception if mapping returns null', function () { test('throws an exception if mapping returns null', function () {
expect(fn() => Option::Some('oof')->map(fn($it) => null))->toThrow(InvalidArgumentException::class); expect(fn() => Option::Some('oof')->map(fn($it) => null))->toThrow(InvalidArgumentException::class);
@@ -155,7 +155,7 @@ describe('->filter()', function () {
return true; return true;
}); });
expect($filtered) expect($filtered)
->isNone()->toBeTrue() ->isNone->toBeTrue()
->and($tattle->called)->toBeFalse() ->and($tattle->called)->toBeFalse()
->and($filtered)->toBe($none); ->and($filtered)->toBe($none);
}); });
@@ -163,12 +163,12 @@ describe('->filter()', function () {
$some = Option::Some(12); $some = Option::Some(12);
$filtered = $some->filter(fn($it) => $it % 2 === 0); $filtered = $some->filter(fn($it) => $it % 2 === 0);
expect($filtered) expect($filtered)
->isSome()->toBeTrue() ->isSome->toBeTrue()
->get()->toBe(12) ->value->toBe(12)
->and($filtered)->toBe($some); ->and($filtered)->toBe($some);
}); });
test('returns None when filter is not matched', function () { test('returns None when filter is not matched', function () {
expect(Option::Some(23)->filter(fn($it) => $it % 2 === 0)->isNone())->toBeTrue(); expect(Option::Some(23)->filter(fn($it) => $it % 2 === 0)->isNone)->toBeTrue();
}); });
}); });
@@ -186,14 +186,14 @@ describe('->tap()', function () {
$value = ''; $value = '';
$original = Option::None(); $original = Option::None();
$tapped = $original->tap( $tapped = $original->tap(
function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); function (Option $it) use (&$value) { $value = $it->isSome ? $it->value : 'none'; });
expect($value)->toBe('none')->and($original)->toBe($tapped); expect($value)->toBe('none')->and($original)->toBe($tapped);
}); });
test('is called for Some', function () { test('is called for Some', function () {
$value = ''; $value = '';
$original = Option::Some('testing'); $original = Option::Some('testing');
$tapped = $original->tap( $tapped = $original->tap(
function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; }); function (Option $it) use (&$value) { $value = $it->isSome ? $it->value : 'none'; });
expect($value)->toBe('testing')->and($original)->toBe($tapped); expect($value)->toBe('testing')->and($original)->toBe($tapped);
}); });
}); });
@@ -225,7 +225,7 @@ describe('->toPhpOption()', function () {
describe('::Some()', function () { describe('::Some()', function () {
test('creates a Some option when given a value', function () { test('creates a Some option when given a value', function () {
expect(Option::Some('hello'))->not->toBeNull()->isSome()->toBeTrue(); expect(Option::Some('hello'))->not->toBeNull()->isSome->toBeTrue();
}); });
test('throws an exception when given null', function () { test('throws an exception when given null', function () {
expect(fn() => Option::Some(null))->toThrow(InvalidArgumentException::class); expect(fn() => Option::Some(null))->toThrow(InvalidArgumentException::class);
@@ -234,23 +234,23 @@ describe('::Some()', function () {
describe('::None()', function () { describe('::None()', function () {
test('creates a None option', function () { test('creates a None option', function () {
expect(Option::None())->not->toBeNull()->isNone()->toBeTrue(); expect(Option::None())->not->toBeNull()->isNone->toBeTrue();
}); });
}); });
describe('::of()', function () { describe('::of()', function () {
test('creates a None option when given null', function () { test('creates a None option when given null', function () {
expect(Option::of(null))->not->toBeNull()->isNone()->toBeTrue(); expect(Option::of(null))->not->toBeNull()->isNone->toBeTrue();
}); });
test('creates a Some option when given a value', function () { test('creates a Some option when given a value', function () {
expect(Option::of('test'))->not->toBeNull() expect(Option::of('test'))->not->toBeNull()
->isSome()->toBeTrue() ->isSome->toBeTrue()
->get()->toBe('test'); ->value->toBe('test');
}); });
test('creates a None option when given PhpOption\None', function () { test('creates a None option when given PhpOption\None', function () {
expect(Option::of(None::create()))->isNone()->toBeTrue(); expect(Option::of(None::create()))->isNone->toBeTrue();
}); });
test('creates a Some option when given PhpOption\Some', function () { test('creates a Some option when given PhpOption\Some', function () {
expect(Option::of(Some::create('something')))->isSome()->toBeTrue()->get()->toBe('something'); expect(Option::of(Some::create('something')))->isSome->toBeTrue()->value->toBe('something');
}); });
}); });

View File

@@ -8,55 +8,55 @@ declare(strict_types=1);
use BitBadger\InspiredByFSharp\{Option, Result}; use BitBadger\InspiredByFSharp\{Option, Result};
describe('->getOK()', function () { describe('->ok', function () {
test('returns OK value for OK result', function () { test('returns OK value for OK result', function () {
expect(Result::OK('yay')->getOK())->toBe('yay'); expect(Result::OK('yay')->ok)->toBe('yay');
}); });
test('throws an exception for Error result', function () { test('throws an exception for Error result', function () {
expect(fn() => Result::Error('whoops')->getOK())->toThrow(InvalidArgumentException::class); expect(fn() => Result::Error('whoops')->ok)->toThrow(InvalidArgumentException::class);
}); });
}); });
describe('->getError()', function () { describe('->error', function () {
test('throws an exception for OK result', function () { test('throws an exception for OK result', function () {
expect(fn() => Result::OK('yeah')->getError())->toThrow(InvalidArgumentException::class); expect(fn() => Result::OK('yeah')->error)->toThrow(InvalidArgumentException::class);
}); });
test('returns Error value for Error result', function () { test('returns Error value for Error result', function () {
expect(Result::Error('boo')->getError())->toBe('boo'); expect(Result::Error('boo')->error)->toBe('boo');
}); });
}); });
describe('->isOK()', function () { describe('->isOK', function () {
test('returns true for OK result', function () { test('returns true for OK result', function () {
expect(Result::OK('ok')->isOK())->toBeTrue(); expect(Result::OK('ok')->isOK)->toBeTrue();
}); });
test('returns false for Error result', function () { test('returns false for Error result', function () {
expect(Result::Error('error')->isOK())->toBeFalse(); expect(Result::Error('error')->isOK)->toBeFalse();
}); });
}); });
describe('->isError()', function () { describe('->isError', function () {
test('returns false for OK result', function () { test('returns false for OK result', function () {
expect(Result::OK('fine')->isError())->toBeFalse(); expect(Result::OK('fine')->isError)->toBeFalse();
}); });
test('returns true for Error result', function () { test('returns true for Error result', function () {
expect(Result::Error('not ok')->isError())->toBeTrue(); expect(Result::Error('not ok')->isError)->toBeTrue();
}); });
}); });
describe('->bind()', function () { describe('->bind()', function () {
test('returns OK when binding against OK with OK', function () { test('returns OK when binding against OK with OK', function () {
expect(Result::OK('one')->bind(fn($it) => Result::OK("$it two"))) expect(Result::OK('one')->bind(fn($it) => Result::OK("$it two")))
->isOK()->toBeTrue()->getOK()->toBe('one two'); ->isOK->toBeTrue()->ok->toBe('one two');
}); });
test('returns Error when binding against OK with Error', function () { test('returns Error when binding against OK with Error', function () {
expect(Result::OK('three')->bind(fn($it) => Result::Error('back to two'))) expect(Result::OK('three')->bind(fn($it) => Result::Error('back to two')))
->isError()->toBeTrue()->getError()->toBe('back to two'); ->isError->toBeTrue()->error->toBe('back to two');
}); });
test('returns Error when binding against Error', function () { test('returns Error when binding against Error', function () {
$original = Result::Error('oops'); $original = Result::Error('oops');
$result = $original->bind(fn($it) => Result::OK('never mind - it worked!')); $result = $original->bind(fn($it) => Result::OK('never mind - it worked!'));
expect($result->isError())->toBeTrue() expect($result->isError)->toBeTrue()
->and($result)->toBe($original); ->and($result)->toBe($original);
}); });
}); });
@@ -94,7 +94,7 @@ describe('->exists()', function () {
describe('->map()', function () { describe('->map()', function () {
test('maps value for OK', function () { test('maps value for OK', function () {
expect(Result::OK('yard')->map(fn($it) => strrev($it))) expect(Result::OK('yard')->map(fn($it) => strrev($it)))
->isOK()->toBeTrue()->getOK()->toBe('dray'); ->isOK->toBeTrue()->ok->toBe('dray');
}); });
test('throws an exception for OK when mapping result is null', function () { test('throws an exception for OK when mapping result is null', function () {
expect(fn() => Result::OK('not null')->map(fn($it) => null))->toThrow(InvalidArgumentException::class); expect(fn() => Result::OK('not null')->map(fn($it) => null))->toThrow(InvalidArgumentException::class);
@@ -108,7 +108,7 @@ describe('->map()', function () {
return 'hello'; return 'hello';
}); });
expect($mapped) expect($mapped)
->isError()->toBeTrue() ->isError->toBeTrue()
->and($tattle->called)->toBeFalse() ->and($tattle->called)->toBeFalse()
->and($mapped)->toBe($error); ->and($mapped)->toBe($error);
}); });
@@ -124,13 +124,13 @@ describe('->mapError()', function () {
return 'hello'; return 'hello';
}); });
expect($mapped) expect($mapped)
->isOK()->toBeTrue() ->isOK->toBeTrue()
->and($tattle->called)->toBeFalse() ->and($tattle->called)->toBeFalse()
->and($mapped)->toBe($ok); ->and($mapped)->toBe($ok);
}); });
test('maps value for Error', function () { test('maps value for Error', function () {
expect(Result::Error('taco')->mapError(fn($it) => str_repeat('*', strlen($it)))) expect(Result::Error('taco')->mapError(fn($it) => str_repeat('*', strlen($it))))
->isError()->toBeTrue()->getError()->toBe('****'); ->isError->toBeTrue()->error->toBe('****');
}); });
test('throws an exception for Error when mapping result is null', function () { test('throws an exception for Error when mapping result is null', function () {
expect(fn() => Result::Error('pizza')->mapError(fn($it) => null))->toThrow(InvalidArgumentException::class); expect(fn() => Result::Error('pizza')->mapError(fn($it) => null))->toThrow(InvalidArgumentException::class);
@@ -161,10 +161,10 @@ describe('->toArray()', function () {
describe('->toOption()', function () { describe('->toOption()', function () {
test('returns a Some option for OK', function () { test('returns a Some option for OK', function () {
expect(Result::OK(99)->toOption())->isSome()->toBeTrue()->get()->toBe(99); expect(Result::OK(99)->toOption())->isSome->toBeTrue()->value->toBe(99);
}); });
test('returns a None option for Error', function () { test('returns a None option for Error', function () {
expect(Result::Error('file not found')->toOption())->isNone()->toBeTrue(); expect(Result::Error('file not found')->toOption())->isNone->toBeTrue();
}); });
}); });
@@ -173,7 +173,7 @@ describe('->tap()', function () {
$value = ''; $value = '';
$original = Result::OK('working'); $original = Result::OK('working');
$tapped = $original->tap(function (Result $it) use (&$value) { $tapped = $original->tap(function (Result $it) use (&$value) {
$value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); $value = $it->isOK ? 'OK: ' . $it->ok : 'Error: ' . $it->error;
}); });
expect($value)->toBe('OK: working')->and($tapped)->toBe($original); expect($value)->toBe('OK: working')->and($tapped)->toBe($original);
}); });
@@ -181,7 +181,7 @@ describe('->tap()', function () {
$value = ''; $value = '';
$original = Result::Error('failed'); $original = Result::Error('failed');
$tapped = $original->tap(function (Result $it) use (&$value) { $tapped = $original->tap(function (Result $it) use (&$value) {
$value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError(); $value = $it->isOK ? 'OK: ' . $it->ok : 'Error: ' . $it->error;
}); });
expect($value)->toBe('Error: failed')->and($tapped)->toBe($original); expect($value)->toBe('Error: failed')->and($tapped)->toBe($original);
}); });
@@ -189,7 +189,7 @@ describe('->tap()', function () {
describe('::OK()', function () { describe('::OK()', function () {
test('creates an OK result for a non-null value', function () { test('creates an OK result for a non-null value', function () {
expect(Result::OK('something'))->isOK()->toBeTrue()->getOK()->toBe('something'); expect(Result::OK('something'))->isOK->toBeTrue()->ok->toBe('something');
}); });
test('throws an exception for OK with a null value', function () { test('throws an exception for OK with a null value', function () {
expect(fn() => Result::OK(null))->toThrow(InvalidArgumentException::class); expect(fn() => Result::OK(null))->toThrow(InvalidArgumentException::class);
@@ -198,7 +198,7 @@ describe('::OK()', function () {
describe('::Error()', function () { describe('::Error()', function () {
test('creates an Error result for a non-null value', function () { test('creates an Error result for a non-null value', function () {
expect(Result::Error('sad trombone'))->isError()->toBeTrue()->getError()->toBe('sad trombone'); expect(Result::Error('sad trombone'))->isError->toBeTrue()->error->toBe('sad trombone');
}); });
test('throws an exception for Error with a null value', function () { test('throws an exception for Error with a null value', function () {
expect(fn() => Result::Error(null))->toThrow(InvalidArgumentException::class); expect(fn() => Result::Error(null))->toThrow(InvalidArgumentException::class);