Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Maybe in reaction to @marcoshuttle's http://marcosh.github.io/post/2017/06/16/maybe-in-php.html
<?php declare(strict_types=1);
// In reaction to @marcoshuttle's http://marcosh.github.io/post/2017/06/16/maybe-in-php.html
// Warning: none of this code has been tested or even run.
namespace Verraes\Maybe;
interface Just extends Maybe {
// We can only extract if we know it's a Just
function extract();
// "extract" fits my mental model better than "get"
function exclusion(Just $a);
}
interface Nothing extends Maybe {
const type="Nothing";
// no extract because extracting from nothing is meaningless
function exclusion(Nothing $a);
}
interface Maybe {
// we only know it's a maybe, so we must provide a default in case it's Nothing
function fromMaybe($default);
// To get a null, we must explicitly pass null as a default
// fromMaybe is the name haskell uses. getOrElse also works.
// These 3 are not relevant to Marco's post, I added them for fun and completeness.
function bind(callable $f) : Maybe;
function then(callable $f);
function map(callable $f) : Maybe;
}
// Constructor functions for Just and Nothing. We could put these in a MaybeFactory or
// whatever but they're really just functions.
function nothing() : Maybe // we're not using Nothing as a return type here, see below
{
return new class() implements Nothing {
function fromMaybe ($default) {
return $default;
}
function bind (callable $f): Maybe {
return nothing();
}
function then (callable $f) {
// no op
}
function map (callable $f) : Maybe{
return nothing();
}
function exclusion (Nothing $a) {}
};
}
function just($value) : Maybe // we're not using Just as a return type here, see below
{
return new class($value) implements Just
{
private $value;
function __construct($value)
{
$this->value = $value;
}
function extract()
{
return $this->value;
}
// I don't have a problem with this "useless" argument, it matches the
// fact that we can't know whether $default will be used or not.
function fromMaybe ($_) {
$this->extract();
}
// $f($x):Maybe
function bind (callable $f): Maybe {
return $f($this->extract());
}
function then (callable $f) {
return $f();
}
function map (callable $f): Maybe {
return just($f($this->extract()));
}
function exclusion (Just $a) {}
};
}
/**
* Property 1
* >> when you have a Maybe a you are sure it is or a Just a or a Nothing,
* >> there are no other possibilities;
*
* Mostly satisfied. You could add more "interface X extends Maybe" if you
* wanted to, but existing code would not be affected.
*
* Property 2
* >> when you have a Maybe a you are obliged to consider both cases;
*
* By default, neither does Haskell.
*
* f :: Maybe a -> a
* f (Just x) = x
*
* > f (Just 5) -- 5
* > f Nothing -- exception
*
* IIRC there's a flag to turn it on. That said, being obliged is better.
*
* extract() is only defined on Just, so that helps somewhat.
* just() and nothing() return a Maybe, so the IDE (in lieue of a static
* typechecker) can't allow us to assume there's an extract().
*
* If you don't consider both cases, you'd fail late, at runtime.
*
* Property 3
* >> when you have a Nothing you don’t have any operation that allows you to
* >> retrieve something of type a.
*
* Check.
*
* I'd like to add a 4th property:
* >> A Maybe is never both Just and Nothing at the same time.
*
* The exclusion() method makes sure that a class can never implement both Just
* and Nothing, because their types will conflict.
*
*
* Conclusion:
* I'd argue my solution is slightly safer than Marco's or others I've seen.
* It's late and I've had some wine and I didn't verify anything so I might be wrong.
*/
@mathiasverraes

This comment has been minimized.

Copy link
Owner Author

commented Oct 26, 2017

// PHPStorm correctly gives a warning: 
function f(Maybe $m) {
    $x = $m->extract(); // Method extract not found in Maybe
}

// PHPStorm correctly gives no warning:
function g(Maybe $m) {
    if($m instanceof Just) $x = $m->extract();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.