ascetik/hypothetik

自制 Maybe monad

v0.3.0 2024-01-16 17:30 UTC

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 是由 HypothetikOption 实例共享的一般接口。

  • 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 实例之外没有用处。

可用的实现

finalMaybeMaybe 类是本包的主要工具。它处理可能包含值的 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 构造函数是私有的。请参见以下示例以了解实例化方法。

finalWhen : (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) 方法来构建实例。

finalEither

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 实现公开供使用,在其他任何上下文中都不实用。

finalNone 是一个 "null value" OptionfinalSome 是一个非 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 接口确保完全可替换的实例,提供从 MaybeWhen 或反向链式调用方法的能力。

这里有一个简单的例子

$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 一样。

组合 MaybeWhen 实例调用

$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 实例。如果需要,我会考虑这种实现...

可能还有一些测试仍然缺失。我不确定是否涵盖了所有可能的使用情况。