ascetik / hypothetik
自制 Maybe monad
Requires
- ascetik/callapsule: ^0.4.0
Requires (Dev)
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2024-09-16 19:00:36 UTC
README
[英文]
自制的面向对象 "Monad",用于更轻松地管理假设值。
发布说明
版本 0.3.0:仍为草案版本。
PHP 版本:8.2.14
- 新的 Hypothetik 接口,描述了此包中包含的 monad 的行为。
- Maybe 类实现了 Hypothetik 接口
- 新的 When,用于处理布尔值的 Hypothetik 实现。
描述
接口
OptionnalValue 是由 Hypothetik 和 Option 实例共享的一般接口。
- OptionnalValue::isValid(): bool:检查值的有效性(!null & !false)
- OptionnalValue::value(): mixed:返回 Option 的原始值
Hypothetik 接口描述了使用可调用对象处理可能为 null 或 false 的值的方式。
- Hypothetik::apply(callable, ...mixed): mixed:使用 Option 值返回给定可调用对象的结果
- Hypothetik::either(callable, ...mixed): Either:根据 Option 返回一个 Either 实例
- Hypothetik::then(callable, ...mixed): Hypothetik:返回一个包含给定可调用对象结果的新的 Hypothetic 实例
- Hypothetik::otherwise(mixed): Hypothetik:如果 Option 值无效,则返回一个替代值
Option 接口描述了包含预期值的实例的行为
- Option::apply(callable, ?array): mixed:使用 Option 值作为第一个参数返回给定函数的结果
- Option::equals(Option): bool:检查与另一个 Option 的相等性
- Option::isValid(): bool:见 OptionnalValue 接口
- Option::value(): mixed:见 OptionnalValue 接口
此包包含 2 个 Option 实现:final 类 None 和 final 类 Some。任何人都可以构建 Option 的另一个实现来替换 Some 类。这就是为什么在此处公开此接口的原因。
Option 是一个简单的 ValueObject,具有简单的行为,在 Hypothetik 实例之外没有用处。
可用的实现
final 类 Maybe:Maybe 类是本包的主要工具。它处理可能包含值的 Option,也可能不包含,并驱动对此值的操作,或不...
- Maybe::equals(Maybe): bool:检查与另一个 Maybe 实例的相等性。
- Maybe::apply(callable, ...mixed): mixed:见 Hypothetik 接口
- Maybe::either(callable): Either:见 Hypothetik 接口
- Maybe::isValid(): bool:见 OptionnalValue 接口
- Maybe::otherwise(mixed): Hypothetik:见 Hypothetik 接口
- Maybe::then(callable, ...mixed): Hypothetik:见 Hypothetik 接口
- Maybe::value(): mixed:见 OptionnalValue 接口
- 静态 Maybe::not(): Maybe:返回一个具有 None 选项的 Maybe 实例
- 静态 Maybe::of(Option): Maybe:返回一个包含给定 Option 实例的 Maybe 实例。
- 静态 Maybe::some(mixed): Hypothetik : 返回一个给定值的 Hypothetik 实例
Maybe 构造函数是私有的。请参见以下示例以了解实例化方法。
final 类 When : (v.0.3.0) 此实现几乎与 Maybe 一样工作。区别在于 When 包含一个带有 bool 值的 Option,并且一个 falsy Option 被视为无效。
- When::apply(callable, ...mixed): mixed : 查看 Hypothetik 接口
- When::either(callable): Either : 查看 Hypothetik 接口
- When::isValid(): bool : 查看 OptionnalValue 接口
- When::otherwise(mixed): Hypothetik : 查看 Hypothetik 接口
- When::then(callable, ...mixed): Hypothetik : 查看 Hypothetik 接口
- When::value(): mixed : 查看 OptionnalValue 接口
- 静态 When::ever(bool): When : 返回一个给定值的 Maybe 实例
私有构造函数。使用 When::ever(bool) 或 Maybe::some(bool) 方法来构建实例。
final 类 Either
Either 类处理一个根据 Maybe Option 值执行的函数。
- Either::or(callable, ...mixed): Either : 如果 maybe 的值是 null,则返回一个新的 Either 实例。
- Either::try(): Maybe : 返回一个新的 Maybe 实例,包含当前 Either 函数的结果。
- Either::value(): mixed : 返回 Maybe 中包含的值
- Either::static use(Maybe, callable, ...mixed): Either : 获取 Either 实例,构造函数私有
Either 实例由 Hypothetik 实现公开供使用,在其他任何上下文中都不实用。
final 类 None 是一个 "null value" Option。 final 类 Some 是一个非 null 值 Option。
用法
构造
由于 Maybe 构造函数不可访问,提供了 3 个工厂方法
$not = Maybe::not(); // Maybe<null> $some = Maybe::some('my value'); // Maybe<string> $someobj = Maybe::some(new MyOwnInstance()); // Maybe<MyOwnInstance> $nullAnyway = Maybe::some(null); // Maybe<null> $any = Maybe::of(new MyOwnStringOption('any string value')); // Maybe<string> $anyobj = Maybe::of(new MyOwnOption(new MyOwnInstance())); // Maybe<MyOwnInstance> $anyNullObj = Maybe::of(new MyOwnNullOption()); // Maybe<null> // version 0.3.0 $truthy = Maybe::some(true); // this is a truthy "When" instance $falsy = Maybe::some(false); // this is a falsy "When" instance
有效值:非 null 值
从前面示例中的 "$some" Maybe 实例中检索原始可选值
echo $some->value(); // "my value"
通过一个函数传递可选值并获取结果
echo $some->apply(strtoupper(...)); // "MY VALUE"
Option 值始终作为第一个参数传递给函数。
可以添加参数,参数之间用逗号分隔。参数的顺序很重要。
另一个带有一些额外参数的示例
$pathToAboutPage = Maybe::some('/about'); echo $pathToAboutPage->apply(trim(...), '/'); // "about", without forward slash $function = fn(string $value, string $separator, string $add)=> trim($value, $separator) . '-' . $add echo $pathToAboutPage->apply($function, '/','page' ); //"about-page"
可以获取一个包含函数结果的新的 Hypothetik 实例。再次强调,Option 值始终作为第一个参数传递给函数,并且可以添加参数
$maybeThen = $some->then(strtoupper(...)); // retourne un nouveau Maybe contenant "MY VALUE" echo $maybeThen->value(); // affiche "MY VALUE"
由于返回了一个新的 Maybe 实例,因此我们可以链式调用此方法
echo $some->then(strtoupper(...)) // return a new Maybe containing "MY VALUE" ->then(fn(string $value) => $value.' is not null') ->value(); // "MY VALUE is not null"
无效值:null 值
对于 null 值,情况略有不同。`apply()` 和 `value()` 方法都将再次返回 null。`then()` 方法返回一个带有 null Option 值的 Maybe。
查看第一个示例中的 "$not" 实例
echo $not->value(); // prints nothing because null echo $not->apply(strtoupper(...)); // null too, function is not applied echo $not->then(strtoupper(...))->value(); // still null
Maybe 提供了一种使用 `otherwise` 方法将 "invalid" 实例替换为有效实例的方法
$otherwise = $not->otherwise('nothing'); echo $otherwise->value(); // prints "nothing" echo $otherwise->apply(strtoupper(...)); // prints "NOTHING" echo $otherwise->then(strtoupper(...)) // // Maybe<'NOTHING'> ->value(); // "NOTHING" again.
其他一些链式调用的示例
echo $not->then(strtoupper(...)) // run strtoupper with a Maybe<null> won't work ->otherwise('i replace null') // new Maybe<string> available after first then() call ->then(fn(string $value) => $value . ' for demonstration') // run the function with the new instance ->value(); // prints "i replace null for demonstration" echo $not->otherwise('i replace null') // new Maybe<string> available ->then(strtoupper(...)) // now transform initial string to upper case ->then(fn(string $value) => $value . ' for demonstration') // and append another string to the previous value ->value(); // prints "I REPLACE NULL for demonstration"
`otherwise` 方法仅在值为 null 时应用。因此
echo $some->otherwise('my other value') // initial $some instance returned ->then(strtoupper(...)) ->then(fn(string $value) => $value . ' for demonstration') ->value(); // prints "MY VALUE for demonstration"
当然,我们已经了解了上述示例中实例的内容。在运行时,我们只能假设我们的值可能是 null。有时,`then()` 和 `otherwise()` 并不足以完成我们想要的工作。另一种可能性是使用 `either()`
// with Some value echo $some->either(toUpperCase(...)) ->or(fn() => 'late value') ->value(); // prints "MY VALUE" // with None value echo $not->either(toUpperCase(...)) ->or(fn() => 'late value') ->value(); // prints "late value"
并从 Either 获取一个新的 Hypothetik 实例
// with Some value echo $some->either(toUpperCase(...)) ->or(fn() => 'late value') ->try() // returns a Maybe<string> from "$some" value ->then(fn(string $value) => $value . ' for demonstration') ->value(); // prints "MY VALUE for demonstration" // with None value echo $not->either(toUpperCase(...)) // won't run this function ->or(fn() => 'late value') // returns a new Either instance holding this new function ->try() // returns a Maybe<string> with "late value' ->then(fn(string $value) => $value . ' for demonstration') // append a string and return another Maybe with new complete string ->value(); // prints "late value for demonstration"
布尔值
假设布尔值的工作方式不同。它始终包含一个带有布尔值的 Option,其中 false 是无效的。Hypothetik 接口确保完全可替换的实例,提供从 Maybe 到 When 或反向链式调用方法的能力。
这里有一个简单的例子
$phrase = 'this is just a test'; $when = Maybe::some(str_contains($phrase, 'just')); // truthy When echo $when->value() ? 'valid' : 'invalid'; // 'valid' $whenNot = Maybe::some(str_contains($phrase, 'only')); // falsy When echo $whenNot->value() ? 'valid' : 'invalid'; // 'invalid'
When 类有自己的静态工厂方法
$when = When::ever(true); // or false...
这次,方法 apply() 和 then 不会将布尔值作为第一个函数参数,允许使用逗号分隔的附加参数,就像 Maybe 一样。
组合 Maybe 和 When 实例调用
$truthyWhen = Maybe::some('/about') // instance of Maybe</about> ->then(fn (string $value) => str_starts_with($value, '/')) // instance of When<false> ->either(fn() => 'truthy result') // will be executed ->or(fn() => 'falsy result') // won't be executed ->try(); // Maybe<'truthy result'> echo $when->value(); // 'truthy result' $falsyWhen = Maybe::some('/about') // instance of Maybe</about> ->then(fn (string $value) => trim($value, '/')) // instance of Maybe<about> ->then(fn (string $value) => str_starts_with($value, '/')) // instance of When<false> ->either(fn() => 'truthy result') // won't be executed ->or(fn() => 'falsy result') // will be executed ->value(); // raw value echo $when; // 'falsy result'
注意
没有依赖注入。如果需要,用户必须提供所需的实例。一个 Maybe 不能携带另一个 Hypothetik,一个 Option 不能携带另一个 Option。尝试这样做将返回给定的实例不变。
问题
我仍然无法正确使用 Php 文档来提供来自任何 IDE 的自动完成。在处理泛型类型时存在问题。
我仍然不需要任何 Hypothetik 容器来处理多个 Hypothetik 实例。如果需要,我会考虑这种实现...
可能还有一些测试仍然缺失。我不确定是否涵盖了所有可能的使用情况。