Created
October 26, 2017 20:03
-
-
Save mathiasverraes/b54c2c32fb66f4c6f739e8dff128a4f0 to your computer and use it in GitHub Desktop.
Maybe in reaction to @marcoshuttle's http://marcosh.github.io/post/2017/06/16/maybe-in-php.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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. | |
*/ |
Author
mathiasverraes
commented
Oct 26, 2017
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment