, (*1)
A Maybe monad implementation for PHP
This project was wholly inspired by
a blog post
by @linepogl., (*2)
Motivation
Dealing with null values (and, in PHP, falsy values) is tedious and prone
to developer error (viz the null pointer exception, trying to dereference
a null)., (*3)
In my exposure to Haskell, I learned about the awesomeness of pattern matching,
whereby you can get the compiler to force yourself to handle all possibilities.
This combines with a tool called Maybe to require specific handling for
"null" and "non-null" possibilities., (*4)
PHP does not offer pattern matching, but we can still use classes to wrap raw
values, and require us to handle null conditions, without repeated explicit
null checking and conditionals., (*5)
Examples
Simple
Before:, (*6)
$blogpost = $repository->get($blogpostId);
echo $blogpost->teaser(); // oh noe! what if $blogpost is null?! :boom:
After:, (*7)
$blogpost = new \Yitznewton\Maybe\Maybe($repository->get($blogpostId));
echo $blogpost->select(function ($bp) { $bp->teaser(); })->valueOr('No blogpost found');
With callback
$blogpost = new \Yitznewton\Maybe\Maybe($repository->get($blogpostId));
$callback = function () {
return someExpensiveOperation();
};
echo $blogpost->select(function ($bp) { $bp->teaser(); })->valueOrCallback($callback);
Loose-falsy
// $process->execute() normally returns a result object, but sometimes returns false
$result = new LooseMaybe($process->execute());
echo $result->select(function ($resultObject) { $resultObject->getStatus(); })->valueOr('failed');
// echoes 'failed' when the result was false
In a simple test using PHP 5.5, performance was approximately 20% that of
a straight is_null() check in an if/else conditional. In other words it
takes 5 times as long to run., (*8)
You can reproduce the test locally by running the profiling testsuite in
PHPUnit. You will first need to install XHProf, and override the XHProf lib
directory using a local phpunit.xml config., (*9)
$ ./vendor/bin/phpunit --testsuite=profiling
Dictionary wrapper
maybe-php includes an array wrapper called Dictionary, whereby trying to
access properties on the wrapper will return a Maybe object. You can specify
whether to return a plain Maybe (default) or a LooseMaybe., (*10)
$dictionary = new \Yitznewton\Maybe\Dictionary([
'foo' => 'bar',
]);
$dictionary->foo->valueOr('quux'); // 'bar'
$dictionary->noSuchKey->valueOr('quux'); // 'quux'
// with LooseMaybe
$dictionary = new \Yitznewton\Maybe\Dictionary([
'foo' => false,
], \Yitznewton\Maybe\LooseMaybe::class);
$dictionary->foo->valueOr('quux'); // 'quux', because loose falsy