Maybe¶
A Maybe
monad represents the possible absence of a value.
This is an equivalent of a nullable value, but a more faithful representation would be an array
containing 0 or 1 value.
In essence:
use Innmind\Immutable\Maybe;
$valueExist = 42;
$valueDoesNotExist = null;
// or
$valueExist = [42];
$valueDoesNotExist = [];
// becomes
$valueExist = Maybe::just(42);
$valueDoesNotExist = Maybe::nothing();
Each example will show how to use Maybe
and the imperative equivalent in plain old PHP (1).
- The nullable approach is used as it's the dominant approach in PHP programs.
Usage¶
In this function we represent the fact that they're may be not a User
(1) for every id. To work with the user, if there's any, you would do:
- This is a fake class.
As you can see the 2 approaches are very similar for now.
In this example the user is directly used as an argument to a function but we often want to extract some data before calling some function. A use case could be to extract the brother id out of this user (1) and call again our function.
- Via a method
function getBrotherId(): int
.
This example introduces the map
and flatMap
methods. They behave the same way as their Sequence
counterpart.
map
will apply the function in case theMaybe
contains a value-
flatMap
is similar tomap
except that the function passed to it must return aMaybe
, instead of having the return typeMaybe<Maybe<User>>
(1) you'll have aMaybe<User>
- as you would by using
map
instead offlatMap
- as you would by using
What this example shows is that with Maybe
you only need to deal with the possible absence of the data when you extract it. While with the imperative style you need to deal with it each time you call a function.
This becomes even more flagrant if the method that returns the brother id itself may not return a value (1). The signature becomes function getBrotherId(): Maybe<int>
.
- as one may not have one
getUser(42)
->flatMap(static fn(User $user) => $user->getBrotherId()) #(1)
->flatMap(static fn(int $id) => getUser($id))
->match(
static fn(User $brother) => doStuff($brother),
static fn() => brotherDoesNotExist(),
);
- This is the only change,
map
is replaced byflatMap
do deal with the possible absence.
So far we do nothing in case our user doesn't have a brother. But what if we want to check if he has a sister in case he doesn't have a brother ? Maybe
has an expressive way to describe such case:
$user = getUser(42);
if (\is_null($user)) {
brotherDoesNotExist();
return;
}
$siblingId = $user->getBrotherId() ?? $user->getSisterId();
if (\is_null($siblingId)) {
brotherDoesNotExist();
return;
}
$sibling = getUser($siblingId);
if (\is_null($sibling)) {
brotherDoesNotExist();
return;
}
doStuff($sibling);
In the ecosystem¶
Maybe
is used to express the abscence of data (1) or the possible failure of an operation (2). For the latter it is expressed via Maybe<Innmind\Immutable\SideEffect>
, meaning if it contains a SideEffect
the operation as succeeded otherwise it failed.
- Such as the absence of a file on the filesystem or the absence of an entity from a storage.
- Such as failing to upload a file to an S3 bucket.
It also has a deferred mode like Sequence
that allows to not directly load in memory a value when you call $sequence->get($index)
. The returned Maybe
in this case will load the value when you call the match
method.