├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Constructor │ ├── Just.php │ └── Nothing.php └── Maybe.php └── test ├── ApTest.php ├── ChainTest.php ├── ConcatTest.php ├── ConstructTest.php ├── EmptyTest.php ├── EqualsTest.php ├── ForkTest.php ├── FromNullableTest.php ├── MapTest.php ├── OfTest.php └── ReduceTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | composer.lock 3 | /vendor/ 4 | /build/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: composer install 2 | language: php 3 | php: 4 | - 7.0 5 | - nightly 6 | script: composer test 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [2.0.0] 8 | 9 | ### Added 10 | 11 | - `Maybe::nothing` to construct a `Nothing` instance 12 | - `Maybe::just` to construct a `Just` instance 13 | 14 | ### Removed 15 | 16 | - `Just` and `Nothing` can no longer be constructed using `new` 17 | 18 | ### Changed 19 | 20 | - `::of()` and `::empty()` have been marked `final` to prevent extension 21 | - `Just` and `Nothing` have been marked `final` to prevent extension 22 | 23 | ## [1.0.0] 24 | 25 | - Initial release 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 php-fp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Maybe Monad for PHP. [![Build Status](https://travis-ci.org/php-fp/php-fp-maybe.svg?branch=master)](https://travis-ci.org/php-fp/php-fp-maybe) 2 | 3 | ## Intro 4 | 5 | The Maybe monad is one of the first monads that you're likely to encounter when reading about monadic functional programming and, more generally, some very basic error handling. Its purpose is to model early-abort computation. Take the following example: 6 | 7 | ```php 8 | [ 17 | 'b' => ['c' => 3] 18 | ] 19 | ]; 20 | 21 | prop('c', prop('b', prop('a', $xs))); // 3 22 | ``` 23 | 24 | In this code, we can see that our `prop` method extracts a given key from an array, and we can nest these `prop` calls to extract more nested values. The flaw in this model is seen when the data isn't in the shape we expect. In this instance, you'll run into troubles with undefined array indices. This is where our Maybe monad becomes useful: 25 | 26 | ```php 27 | [ 44 | 'b' => ['c' => 3] 45 | ] 46 | ]; 47 | 48 | prop('a')($xs) 49 | ->chain(prop('b')) 50 | ->chain(prop('c')); // Just 3 51 | 52 | prop('a')($xs) 53 | ->chain(prop('b')) 54 | ->chain(prop('d')); // Nothing 55 | 56 | prop('immediate_mistake')($xs) 57 | ->chain(prop('b')) 58 | ->chain(prop('d')); // Nothing 59 | ``` 60 | 61 | What we can see here is that, when an 'error' occurs, we are given a `Nothing` value, which can be used in exactly the same way as our `Just` value. The key difference is that `Nothing` ignores the chain function and returns `Nothing` again. This means that we can write our full, nesting computation without having to check at each key whether or not the next one exists. When we _do_ find a non-existent key, all further computation is bypassed (hence 'early abort'). 62 | 63 | The merit of should be obvious: the first example I normally give is with a function that returns a single row from a database (if the query has any results): when successful, your function could return a Just constructor for the row, else Nothing. 64 | 65 | ### API 66 | 67 | In the following type signatures, constructors and static functions are written as one would see in pure languages such as Haskell. The others contain a pipe, where the type before the pipe represents the type of the current Maybe instance, and the type after the pipe represents the function. 68 | 69 | ### `of :: a -> Maybe a` 70 | 71 | This is the applicative constructor for the Maybe monad. It returns the value, regardless of what it is, as a `Just` instance. 72 | 73 | ```php 74 | fork('default') == 'test'); 79 | ``` 80 | 81 | ### `empty :: Maybe a` 82 | 83 | This returns a `Nothing` instance, which is useful when concatenating results (e.g. with `reduce`) and you need an initial accumulator. It is also required (along with `concat`) for the `Monoid` definition. 84 | 85 | ```php 86 | concat(Maybe::of('blah')) 92 | ->fork(null); 93 | 94 | assert($value == 'blah'); 95 | ``` 96 | 97 | ### `just :: a -> Just a` 98 | 99 | Standard constructor for `Just` instances. Typically you should call `Maybe::of` instead. 100 | 101 | ### `nothing :: a -> Nothing a` 102 | 103 | Standard constructor for `Nothing` instances. Typically you should call `Maybe::nothing` instead. 104 | 105 | ### `ap :: Maybe (a -> b) | Maybe a -> Maybe b` 106 | 107 | Apply a Maybe-wrapped argument to a Maybe-wrapped function. Nothing values act as the identity function, as shown below: 108 | 109 | ```php 110 | ap(Maybe::of(2)) 124 | ->ap(Maybe::of(8)); 125 | 126 | assert($value->fork(10000) == 10); 127 | assert(Maybe::empty()->ap(Maybe::of(2))->fork(10000) == 2); 128 | ``` 129 | 130 | ### `concat :: Maybe a | Maybe a -> Maybe a` 131 | 132 | Semigroup concatenation for Maybe values. This function should be used to combine two Maybe values into one. 133 | - For two Just instances, the two values are concatenated and wrapped with a Just constructor. 134 | - For one Just and one Nothing, the Just value is returned. 135 | - For two Nothing values, Nothing is returned. 136 | 137 | ```php 138 | concat($x); 149 | }, 150 | Maybe::empty() 151 | ); 152 | 153 | assert($acc->fork(5) == 2); 154 | ``` 155 | 156 | ### `chain :: Maybe a | (a -> Maybe b) -> Maybe b` 157 | 158 | This is the equivalent of Haskell's `>>=` (bind) operation for this Maybe implementation. This runs a Maybe-returning function on the inner value of this Maybe, and returns the result. This is the important mechanism behind the example in the introduction. 159 | 160 | ```php 161 | chain(safeHalf)->fork(null) == 4); 173 | ``` 174 | 175 | ### `equals :: Maybe a | Maybe a -> Bool` 176 | 177 | Setoid equality for Maybe. First, the two constructors are checked for equality. If they are both `Nothing`, they are equal. If they are both `Just`, The inner values are compared. 178 | 179 | ```php 180 | value = $x; 188 | } 189 | 190 | public function equals($that) 191 | { 192 | return $this->value === $that->value; 193 | } 194 | } 195 | 196 | $a = Maybe::empty(); 197 | $b = Maybe::of(new Value(2)); 198 | $c = Maybe::of(new Value(3)); 199 | $d = Maybe::of(new Value(3)); 200 | 201 | assert($a->equals($a) == true); 202 | assert($a->equals($b) == false); 203 | assert($b->equals($c) == false); 204 | assert($c->equals($d) == true); 205 | ``` 206 | 207 | ### `map :: Maybe a | (a -> b) -> Maybe b` 208 | 209 | Standard functor map: transform the value within a Maybe, and return an updated Maybe. 210 | 211 | ```php 212 | map($add2)->fork(-1) == -1); 222 | assert(Maybe::of(2)->map($add2)->fork(-1) == 4); 223 | ``` 224 | 225 | ### `reduce :: Maybe a | (b -> a -> b) -> Maybe a -> b` 226 | 227 | Maybe is a foldable type. For Nothing values, `reduce` returns whatever initial accumulator value was supplied. For `Just` types, the result is the reduction function evaluated with the accumulator and the wrapped value. 228 | 229 | ```php 230 | reduce($append, []) == []); 243 | assert(Maybe::of(2)->reduce($append, []) == [2]); 244 | ``` 245 | 246 | ### `fork :: Maybe a | a -> a` 247 | 248 | Extract the value from within the Maybe. At the end of your fail-able computation, you'll usually want to extract the value from the Maybe. For type safety, this function requires a parameter to return in place of a `Nothing` value (of course, you can just return `null`): 249 | 250 | ```php 251 | fork(4) == 2); 256 | assert(Maybe::empty()->fork(4) == 4); 257 | ``` 258 | 259 | ## Contributing 260 | 261 | This monad is implemented with more variety than IO, as there are many (regularly-used) type classes to which it can be applied, so feel free to add implementations for your favourites. Other than that, the underlying mechanisms are probably far less prone to change. 262 | 263 | I see documentation changes being far more likely, and they are also more than welcome: the aim of the PhpFp project is not only to produce a set of functional utilities, but also to provide a learning resource for programmers wishing to learn more about functional programming. 264 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | { 4 | "name": "Tom", 5 | "email": "tomjharding@live.co.uk" 6 | }, 7 | { 8 | "name": "Woody Gilk", 9 | "email": "woody.gilk@gmail.com", 10 | "role": "Contributor" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "PhpFp\\Maybe\\": "src/" 16 | } 17 | }, 18 | "autoload-dev": { 19 | "psr-4": { 20 | "PhpFp\\Maybe\\Test\\": "test/" 21 | } 22 | }, 23 | "description": "An implementation of the Maybe monad in PHP.", 24 | "homepage": "http://www.github.com/php-fp/php-fp-maybe", 25 | "keywords": ["functional", "monad", "maybe"], 26 | "license": "MIT", 27 | "name": "php-fp/php-fp-maybe", 28 | "scripts": { 29 | "test": "phpunit" 30 | }, 31 | "time": "2016-04-19", 32 | "versions": "1.0.1", 33 | "require-dev": { 34 | "phpunit/phpunit": "^5.7" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test/ 6 | 7 | 8 | 9 | 10 | 11 | src/ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Constructor/Just.php: -------------------------------------------------------------------------------- 1 | chain( 21 | function ($f) use ($that) 22 | { 23 | return $that->map($f); 24 | } 25 | ); 26 | } 27 | 28 | /** 29 | * Semigroup concatenation of two Maybe values. 30 | * @param Maybe $that Inner types must match! 31 | * @return Maybe The concatenated value. 32 | * @todo Make friendly with primitives. 33 | */ 34 | public function concat(Maybe $that) : Maybe 35 | { 36 | if ($that instanceof Nothing) 37 | { 38 | return $this; 39 | } 40 | 41 | return Maybe::of( 42 | $this->value->concat( 43 | $that->fork(null) 44 | ) 45 | ); 46 | } 47 | 48 | /** 49 | * PHP implemenattion of Haskell Maybe's >>=. 50 | * @param callable $f a -> Maybe b 51 | * @return Maybe The result of the function. 52 | */ 53 | public function chain(callable $f) : Maybe 54 | { 55 | return $f($this->value); 56 | } 57 | 58 | /** 59 | * Check whether two Maybe values be equal. 60 | * @param Maybe $that Inner types should match! 61 | * @return bool 62 | * @todo Make this compatible with setoid inner values. 63 | */ 64 | public function equals(Maybe $that) : bool 65 | { 66 | return $that instanceof Just 67 | && $this->value->equals($that->fork(null)); 68 | } 69 | 70 | /** 71 | * Fork this Maybe, with a default for Nothing. 72 | * @param mixed $default In case of Nothing. 73 | * @return mixed Whatever the Maybe's inner type is. 74 | */ 75 | public function fork($_) 76 | { 77 | return $this->value; 78 | } 79 | 80 | /** 81 | * Functor map, derived from chain. 82 | * @param callable $f The mapping function. 83 | * @return Maybe The outer structure is preserved. 84 | */ 85 | public function map(callable $f) : Maybe 86 | { 87 | return $this->chain( 88 | function ($a) use ($f) 89 | { 90 | return Maybe::of($f($a)); 91 | } 92 | ); 93 | } 94 | 95 | /** 96 | * Fold for the Just constructor. 97 | * @param callable $f The folding function. 98 | * @param mixed $x The accumulator value. 99 | * @return mixed The $x type and $f return type. 100 | */ 101 | public function reduce(callable $f, $x) 102 | { 103 | return $f($x, $this->value); 104 | } 105 | 106 | /** 107 | * The inner value for this Maybe. 108 | * @var mixed 109 | */ 110 | private $value = null; 111 | 112 | /** 113 | * Create a Maybe::just instance. 114 | * Don't construct this with a null! 115 | * @param mixed $x The value to wrap. 116 | */ 117 | protected function __construct($value) 118 | { 119 | $this->value = $value; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Constructor/Nothing.php: -------------------------------------------------------------------------------- 1 | Maybe b 36 | * @return Maybe $this (Nothing). 37 | */ 38 | public function chain(callable $_) : Maybe 39 | { 40 | return $this; 41 | } 42 | 43 | /** 44 | * Check whether two Maybe values be equal. 45 | * @param Maybe $that Inferred inner types should match! 46 | * @return bool 47 | */ 48 | public function equals(Maybe $that) : bool 49 | { 50 | return $that instanceof Nothing; 51 | } 52 | 53 | /** 54 | * Fork this Maybe, with a default for Maybe. 55 | * @param mixed $default Returned for Nothing. 56 | * @return mixed For Nothing, this is $default. 57 | */ 58 | public function fork($default) 59 | { 60 | return $default; 61 | } 62 | 63 | /** 64 | * Functor map, derived from chain. 65 | * @param callable $f The mapping function. 66 | * @return Maybe The outer structure is preserved. 67 | */ 68 | public function map(callable $_) : Maybe 69 | { 70 | return $this; 71 | } 72 | 73 | /** 74 | * Fold for Nothing - returns the accumulator. 75 | * @param callable $f The folding function. 76 | * @param mixed $x The accumulator value. 77 | * @return mixed The $x type and $f return type. 78 | */ 79 | public function reduce(callable $_, $x) 80 | { 81 | return $x; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Maybe.php: -------------------------------------------------------------------------------- 1 | >=. 83 | * @param callable $f a -> Maybe b 84 | * @return Maybe The result of the function. 85 | */ 86 | abstract public function chain(callable $f) : Maybe; 87 | 88 | /** 89 | * Check whether two Maybe values be equal. 90 | * @param Maybe $that Inner types should match! 91 | * @return bool 92 | * @todo Make this compatible with setoid inner values. 93 | */ 94 | abstract public function equals(Maybe $that) : bool; 95 | 96 | /** 97 | * Fork this Maybe, with a default for Nothing. 98 | * @param mixed $default In case of Nothing. 99 | * @return mixed Whatever the Maybe's inner type is. 100 | */ 101 | abstract public function fork($default); 102 | 103 | /** 104 | * Functor map, derived from chain. 105 | * @param callable $f The mapping function. 106 | * @return Maybe The outer structure is preserved. 107 | */ 108 | abstract public function map(callable $f) : Maybe; 109 | 110 | /** 111 | * Left fold / reduction for Maybe. 112 | * @param callable $f The folding function. 113 | * @param mixed $x The accumulator value. 114 | * @return mixed The $x type and $f return type. 115 | */ 116 | abstract public function reduce(callable $f, $x); 117 | } 118 | -------------------------------------------------------------------------------- /test/ApTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 1, 18 | 'Ap takes one parameter.' 19 | ); 20 | } 21 | 22 | public function testAp() 23 | { 24 | $add = function ($x) 25 | { 26 | return function ($y) use ($x) 27 | { 28 | return $x + $y; 29 | }; 30 | }; 31 | 32 | $a = Maybe::of(2); 33 | $b = Maybe::of(4); 34 | 35 | $this->assertEquals( 36 | Maybe::of($add)->ap($a)->ap($b) 37 | ->fork(null), 38 | 6, 39 | 'Applies to a Just.' 40 | ); 41 | 42 | $this->assertEquals( 43 | Maybe::empty()->ap($a)->fork(-1), 44 | 2, 45 | 'Applies to a Nothing.' 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/ChainTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 13 | 14 | $this->assertEquals( 15 | $count, 16 | 1, 17 | 'Chain takes one parameter.' 18 | ); 19 | } 20 | 21 | public function testChain() 22 | { 23 | $safeHalf = function ($x) 24 | { 25 | return $x % 2 == 0 26 | ? Maybe::of($x / 2) 27 | : Maybe::empty(); 28 | }; 29 | 30 | $this->assertEquals( 31 | $safeHalf(16) 32 | ->chain($safeHalf) 33 | ->fork(null), 34 | 4, 35 | 'Just chains.' 36 | ); 37 | 38 | $this->assertEquals( 39 | $safeHalf(5) 40 | ->chain($safeHalf) 41 | ->fork(null), 42 | null, 43 | 'Nothing chains.' 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/ConcatTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 1, 18 | 'Concat takes one parameter.' 19 | ); 20 | } 21 | 22 | public function testConcat() 23 | { 24 | $maybes = [Maybe::empty(), Maybe::of(2), Maybe::empty()]; 25 | 26 | $acc = array_reduce( 27 | $maybes, 28 | function ($acc, $x) 29 | { 30 | return $acc->concat($x); 31 | }, 32 | Maybe::empty() 33 | ); 34 | 35 | $this->assertEquals(2, $acc->fork(5), 'Concatenates.'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/ConstructTest.php: -------------------------------------------------------------------------------- 1 | getConstructor()->getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 1, 18 | 'Takes one parameter.' 19 | ); 20 | } 21 | 22 | public function testConstruct() 23 | { 24 | $this->assertEquals( 25 | (Maybe::just(2))->fork(null), 26 | 2, 27 | 'Constructs a Just.' 28 | ); 29 | 30 | $this->assertEquals( 31 | Maybe::nothing()->fork(-1), 32 | -1, 33 | 'Constructs a Nothing.' 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/EmptyTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 13 | 14 | $this->assertEquals( 15 | $count, 16 | 0, 17 | 'Takes no parameters.' 18 | ); 19 | } 20 | 21 | public function testEmpty() 22 | { 23 | $this->assertInstanceOf( 24 | 'PhpFp\Maybe\Constructor\Nothing', 25 | Maybe::empty(), 26 | 'The empty value is a nothing.' 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/EqualsTest.php: -------------------------------------------------------------------------------- 1 | value = $x; 13 | } 14 | 15 | public function equals($that) 16 | { 17 | return $this->value === $that->value; 18 | } 19 | } 20 | 21 | class EqualsTest extends \PHPUnit_Framework_TestCase 22 | { 23 | public function testEqualsParameterCount() 24 | { 25 | $count = (new \ReflectionMethod('PhpFp\Maybe\Constructor\Just::equals')) 26 | ->getNumberOfParameters(); 27 | 28 | $this->assertEquals( 29 | $count, 30 | 1, 31 | 'Equals takes one parameter.' 32 | ); 33 | } 34 | 35 | public function testEquals() 36 | { 37 | $a = Maybe::empty(); 38 | $b = Maybe::of(new Value(2)); 39 | $c = Maybe::of(new Value(3)); 40 | $d = Maybe::of(new Value(3)); 41 | 42 | $this->assertEquals( 43 | $a->equals($a), 44 | true, 45 | 'Two Nothing values.' 46 | ); 47 | 48 | $this->assertEquals( 49 | $a->equals($b), 50 | false, 51 | 'Just and Nothing.' 52 | ); 53 | 54 | $this->assertEquals( 55 | $b->equals($c), 56 | false, 57 | 'Unequal values.' 58 | ); 59 | 60 | $this->assertEquals( 61 | $c->equals($d), 62 | true, 63 | 'Equal values.' 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/ForkTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 1, 18 | 'Fork takes one parameter.' 19 | ); 20 | } 21 | 22 | public function testFork() 23 | { 24 | $this->assertEquals( 25 | Maybe::of(2) 26 | ->fork(4), 27 | 2, 28 | 'Just fork.' 29 | ); 30 | 31 | $this->assertEquals( 32 | Maybe::empty() 33 | ->fork(4), 34 | 4, 35 | 'Nothing fork.' 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/FromNullableTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 13 | 14 | $this->assertEquals( 15 | $count, 16 | 1, 17 | 'Takes one parameter.' 18 | ); 19 | } 20 | 21 | public function testFromNullable() 22 | { 23 | $this->assertInstanceOf( 24 | 'PhpFp\Maybe\Constructor\Nothing', 25 | Maybe::fromNullable(null), 26 | 'Nulls become Nothing values.' 27 | ); 28 | 29 | $this->assertEquals( 30 | Maybe::fromNullable(2)->fork(null), 31 | 2, 32 | 'Anything else becomes Just-wrapped.' 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/MapTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 1, 18 | 'Map takes one parameter.' 19 | ); 20 | } 21 | 22 | public function testMap() 23 | { 24 | $add2 = function ($x) 25 | { 26 | return $x + 2; 27 | }; 28 | 29 | $this->assertEquals( 30 | Maybe::empty() 31 | ->map($add2) 32 | ->fork(-1), 33 | -1, 34 | 'Nothing map.' 35 | ); 36 | 37 | $this->assertEquals( 38 | Maybe::of(2) 39 | ->map($add2) 40 | ->fork(-1), 41 | 4, 42 | 'Just map.' 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/OfTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 13 | 14 | $this->assertEquals( 15 | $count, 16 | 1, 17 | 'Takes one parameter.' 18 | ); 19 | } 20 | 21 | public function testEmpty() 22 | { 23 | $this->assertInstanceOf( 24 | 'PhpFp\Maybe\Constructor\Just', 25 | Maybe::of(2), 26 | 'The empty value is a nothing.' 27 | ); 28 | 29 | $this->assertEquals( 30 | Maybe::of(2)->fork(-1), 31 | 2, 32 | 'Creates a Just value.' 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/ReduceTest.php: -------------------------------------------------------------------------------- 1 | getNumberOfParameters(); 14 | 15 | $this->assertEquals( 16 | $count, 17 | 2, 18 | 'Reduce takes two parameters.' 19 | ); 20 | } 21 | 22 | public function testReduce() 23 | { 24 | $append = function ($xs, $x) 25 | { 26 | return array_merge($xs, [$x]); 27 | }; 28 | 29 | $this->assertEquals( 30 | Maybe::empty() 31 | ->reduce($append, []), 32 | [], 33 | 'Nothing reduction.' 34 | ); 35 | 36 | $this->assertEquals( 37 | Maybe::of(2) 38 | ->reduce($append, []), 39 | [2], 40 | 'Just reduction.' 41 | ); 42 | } 43 | } 44 | --------------------------------------------------------------------------------