[
  {
    "path": ".gitignore",
    "content": "vendor"
  },
  {
    "path": "README.md",
    "content": "MonadPHP\n========\n\nThis is a basic Monad library for PHP.\n\nUsage\n=====\n\nValues are \"wrapped\" in the monad via either the constructor: `new MonadPHP\\Identity($value)` or the `unit()` method on an existing instance: `$monad->unit($value);`\n\nFunctions can be called on the wrapped value using `bind()`:\n\n    use MonadPHP\\Identity;\n    $monad = new Identity(1);\n    $monad->bind(function($value) { var_dump($value); });\n    // Prints int(1)\n\nAll calls to bind return a new monad instance wrapping the return value of the function.\n\n     use MonadPHP\\Identity;\n    $monad = new Identity(1);\n    $monad->bind(function($value) {\n            return 2 * $value;\n        })->bind(function($value) { \n            var_dump($value); \n        });\n    // Prints int(2)\n\nAdditionally, \"extracting\" the raw value is supported as well (since this is PHP and not a pure functional language)...\n\n    use MonadPHP\\Identity;\n    $monad = new Identity(1);\n    var_dump($monad->extract());\n    // Prints int(1)\n\nMaybe Monad\n===========\n\nOne of the first useful monads, is the Maybe monad. The value here is that it will only call the callback provided to `bind()` if the value it wraps is not `null`.\n\n    use MonadPHP\\Maybe;\n    $monad = new Maybe(1);\n    $monad->bind(function($value) { var_dump($value); });\n    // prints int(1)\n\n    $monad = new Maybe(null);\n    $monad->bind(function($value) { var_dump($value); });\n    // prints nothing (callback never called)...\n\nThe included Chain monad does the same thing, but providing a short-cut implementation for objects:\n\n    use MonadPHP\\Chain;\n    $monad = new Chain($someChainableObject);\n    $obj = $monad->call1()->call2()->nonExistantMethod()->call4()->extract();\n    var_dump($obj);\n    // null\n\nThis can prevent errors when used with chaining...\n\nList Monad\n==========\n\nThis abstracts away the concept of a list of items (an array):\n\n    use MonadPHP\\ListMonad;\n    $monad = new ListMonad(array(1, 2, 3, 4));\n    $doubled = $monad->bind(function($value) { return 2 * $value; });\n    var_dump($doubled->extract());\n    // Prints array(2, 4, 6, 8)\n\nNote that the passed in function gets called once per value, so it only ever deals with a single element, never the entire array...\n\nIt also works with any `Traversable` object (like iterators, etc). Just be aware that returning the new monad that's wrapped will alwyas become an array...\n\nComposition\n===========\n\nThese Monads can be composed together to do some really useful things:\n\n    use MonadPHP\\ListMonad;\n    use MonadPHP\\Maybe;\n\n    $monad = new ListMonad(array(1, 2, 3, null, 4));\n    $newMonad = $monad->bind(function($value) { return new Maybe($value); });\n    $doubled = $newMonad->bind(function($value) { return 2 * $value; });\n    var_dump($doubled->extract());\n    // Prints array(2, 4, 6, null, 8)\n\nOr, what if you want to deal with multi-dimensional arrays?\n\n    use MonadPHP\\ListMonad;\n    $monad = new ListMonad(array(array(1, 2), array(3, 4), array(5, 6)));\n    $newMonad = $monad->bind(function($value) { return new ListMonad($value); });\n    $doubled = $newMonad->bind(function($value) { return 2 * $value; });\n    var_dump($doubled->extract());\n    // Prints array(array(2, 4), array(6, 8), array(10, 12))\n\nThere also exist helper constants on each of the monads to get a callback to the `unit` method:\n\n    $newMonad = $monad->bind(Maybe::unit);\n    // Does the same thing as above \n\nReal World Example\n==================\n\nImagine that you want to traverse a multi-dimensional array to create a list of values of a particular sub-key. For example:\n\n    $posts = array(\n        array(\"title\" => \"foo\", \"author\" => array(\"name\" => \"Bob\", \"email\" => \"bob@example.com\")),\n        array(\"title\" => \"bar\", \"author\" => array(\"name\" => \"Tom\", \"email\" => \"tom@example.com\")),\n        array(\"title\" => \"baz\"),\n        array(\"title\" => \"biz\", \"author\" => array(\"name\" => \"Mark\", \"email\" => \"mark@example.com\")),\n    );\n    \nWhat if we wanted to extract all author names from this data set. In traditional procedural programming, you'd likely have a number of loops and conditionals. With monads, it becomes quite simple.\n\nFirst, we define a function to return a particular index of an array:\n\n    function index($key) {\n        return function($array) use ($key) {\n            return isset($array[$key]) ? $array[$key] : null;\n        };\n    }\n    \nBasically, this just creates a callback which will return a particular array key if it exists. With this, we have everything we need to get the list of authors.\n\n    $postMonad = new MonadPHP\\ListMonad($posts);\n    $names = $postMonad\n        ->bind(MonadPHP\\Maybe::unit)\n        ->bind(index(\"author\"))\n        ->bind(index(\"name\"))\n        ->extract();\n        \nFollow through and see what happens!\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"require\": {\n        \"php\": \">=5.3.0\"\n    },\n    \"autoload\": {\n        \"psr-0\": {\n            \"MonadPHP\": \"lib\"\n        }\n    }\n}"
  },
  {
    "path": "lib/MonadPHP/Chain.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass Chain extends Maybe {\n\n    public function __set($name, $value) {\n        return $this->bind(function($obj) use($name, $value) {\n            $obj->$name = $value;\n            return $value;\n        });\n    }\n\n    public function __get($name) {\n        return $this->bind(function($obj) use($name) {\n            if (isset($obj->$name)) {\n                return $obj->$name;\n            }\n            return null;\n        });\n    }\n\n    public function __call($name, array $args = array()) {\n        return $this->bind(function($obj) use ($name, $args) {\n            if (is_callable(array($obj, $name))) {\n                return call_user_func_array(array($obj, $name), $args);\n            }\n            return null;\n        });\n    }\n\n}"
  },
  {
    "path": "lib/MonadPHP/Deferred.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass Deferred extends Promise {\n\n    public function succeed($value) {\n        $this->resolve(true, $value);\n    }\n\n    public function fail($value) {\n        $this->resolve(false, $value);\n    }\n\n}"
  },
  {
    "path": "lib/MonadPHP/Identity.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass Identity extends Monad {\n\n    const unit = \"MonadPHP\\Identity::unit\";\n\n}\n"
  },
  {
    "path": "lib/MonadPHP/ListMonad.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass ListMonad extends Monad {\n\n    const unit = \"MonadPHP\\ListMonad::unit\";\n\n    public function __construct($value) {\n        if (!is_array($value) && !$value instanceof \\Traversable) {\n            throw new \\InvalidArgumentException('Must be traversable');\n        }\n        return parent::__construct($value);\n    }\n\n    public function bind($function, array $args = array()) {\n        $result = array();\n        foreach ($this->value as $value) {\n            $result[] = $this->runCallback($function, $value, $args);\n        }\n        return $this::unit($result);\n    }\n\n    public function extract() {\n        $ret = array();\n        foreach ($this->value as $value) {\n            if ($value instanceof Monad) {\n                $ret[] = $value->extract();\n            } else {\n                $ret[] = $value;\n            }\n        }\n        return $ret;\n    }\n\n}\n"
  },
  {
    "path": "lib/MonadPHP/Maybe.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass Maybe extends Monad {\n\n    const unit = \"MonadPHP\\Maybe::unit\";\n\n    public function bind($function){\n        if (!is_null($this->value)) {\n            return parent::bind($function);\n        }\n        return $this::unit(null);\n    }\n\n}\n"
  },
  {
    "path": "lib/MonadPHP/Monad.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nabstract class Monad {\n\n    protected $value;\n\n    public function __construct($value) {\n        $this->value = $value;\n    }\n\n    public static function unit($value) {\n        if ($value instanceof static) {\n            return $value;\n        }\n        return new static($value);\n    }\n\n    public function bind($function, array $args = array()) {\n        return $this::unit($this->runCallback($function, $this->value, $args));\n    }\n\n    public function extract() {\n        if ($this->value instanceof self) {\n            return $this->value->extract();\n        }\n        return $this->value;\n    }\n\n    protected function runCallback($function, $value, array $args = array()) {\n        if ($value instanceof self) {\n            return $value->bind($function, $args);\n        }\n        array_unshift($args, $value);\n        return call_user_func_array($function, $args);\n    }\n\n}\n"
  },
  {
    "path": "lib/MonadPHP/Promise.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass Promise extends Monad {\n\n    const unit = \"MonadPHP\\Promise::unit\";\n\n    protected $isResolved = false;\n    protected $succeed = null;\n\n    protected $children = array();\n    protected $success;\n    protected $failure;\n\n    public function __construct($success = null, $failure = null) {\n        $this->success = $success;\n        $this->failure = $failure;\n    }\n\n    public static function unit($success = null, $failure = null) {\n        return new Promise($success, $failure); \n    }\n\n    public function bind($success, $failure) {\n        $obj = $this->unit($success, $failure);\n        if ($this->isResolved) {\n            $obj->resolve($this->succeed, $this->value);\n        } else {\n            $this->children[] = $obj;\n        }\n        return $obj;\n    }\n\n    protected function resolve($status, $value) {\n        if ($this->isResolved) {\n            throw new \\BadMethodCallException('Promise already resolved');\n        }\n        $this->value = $value;\n        $this->isResolved = true;\n        $this->succeed = $status;\n        $callback = $status ? $this->success : $this->failure;\n        if ($callback) {\n            $this->value = call_user_func($callback, $value);\n        }\n        foreach ($this->children as $child) {\n            $child->resolve($status, $this->value);\n        }\n    }\n\n    public function when($success = null, $failure = null) {\n        return $this->bind($success, $failure);\n    }\n}\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit backupGlobals=\"true\"\n         backupStaticAttributes=\"true\"\n         bootstrap=\"./vendor/autoload.php\"\n         colors=\"true\"\n         convertErrorsToExceptions=\"true\"\n         convertNoticesToExceptions=\"true\"\n         convertWarningsToExceptions=\"true\"\n         forceCoversAnnotation=\"false\"\n         mapTestClassNameToCoveredClassName=\"false\"\n         processIsolation=\"false\"\n         stopOnError=\"false\"\n         stopOnFailure=\"false\"\n         stopOnIncomplete=\"false\"\n         stopOnSkipped=\"false\"\n         testSuiteLoaderClass=\"PHPUnit_Runner_StandardTestSuiteLoader\"\n         strict=\"true\"\n         verbose=\"false\">\n    <testsuites>\n        <testsuite name=\"Unit\">\n            <directory>./test/</directory>\n        </testsuite>\n    </testsuites>\n    <filter>\n        <whitelist>\n            <directory suffix=\".php\">./lib/</directory>\n        </whitelist>\n    </filter>\n</phpunit>"
  },
  {
    "path": "test/MonadPHP/IdentityTest.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass IdentityTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testBind() {\n        $monad = new Identity(1);\n        $this->assertEquals($monad->unit(1), $monad->bind('intval'));\n    }\n\n    public function testBindUnit() {\n        $monad = new Identity(1);\n        $this->assertEquals($monad, $monad->bind($monad::unit));\n    }\n\n    public function testExtract() {\n        $monad = new Identity(new Maybe(1));\n        $this->assertEquals(1, $monad->extract());\n    }\n\n}\n"
  },
  {
    "path": "test/MonadPHP/ListMonadTest.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass ListMonadTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testUnitFailure() {\n        $this->setExpectedException('InvalidArgumentException');\n        $monad = new ListMonad(123);\n    }\n\n    public function testBindEmpty() {\n        $monad = new ListMonad(array());\n        $called = 0;\n        $monad->bind(function($value) use (&$called) { $called++; return $value; });\n        $this->assertEquals(0, $called);\n    }\n\n    public function testBindNotEmpty() {\n        $monad = new ListMonad(array(1, 2, 3));\n        $called = 0;\n        $new = $monad->bind(function($value) use (&$called) { $called++; return $value; });\n        $this->assertEquals(3, $called);\n        $this->assertEquals($monad, $new);\n        $this->assertEquals(array(1, 2, 3), $new->extract());\n    }\n\n    public function testComposedListMonad() {\n        $monad = new ListMonad(array(1, 2, 3, null, 4));\n        $newMonad = $monad->bind(function ($v) { return new Maybe($v); });\n        $doubled = $newMonad->bind(function ($v) { return $v * 2; });\n        $this->assertEquals(array(2, 4, 6, null, 8), $doubled->extract());\n    }\n\n\n}"
  },
  {
    "path": "test/MonadPHP/MaybeTest.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass MaybeTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testBind() {\n        $monad = new Maybe(1);\n        $this->assertEquals(new Maybe(\"1\"), $monad->bind(\"strval\"));\n        \n        $monad2 = $monad->unit(null);\n        $called = false;\n        $func = function($a) use (&$called) {\n            $called = true;\n        };\n        $this->assertEquals(new Maybe(null), $monad2->bind($func));\n        $this->assertFalse($called);\n    }\n\n    public function testUnit() {\n        $monad = new Maybe(1);\n        $this->assertEquals($monad, $monad->unit($monad));\n    }\n\n    public function testExtract() {\n        $monad = new Maybe(1);\n        $this->assertEquals(1, $monad->extract());\n    }\n\n    public function testSelfUnit() {\n        $monad = new Maybe(1);\n        $m2 = $monad->unit($monad);\n        $m3 = $monad->unit($m2);\n        $this->assertEquals($monad, $m3);\n    }\n\n}"
  },
  {
    "path": "test/MonadPHP/PromiseTest.php",
    "content": "<?php\n\nnamespace MonadPHP;\n\nclass PromiseTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testFail() {\n        $promise = new Deferred;\n        $calledSuccess = false;\n        $calledFailure = false;\n        $promise->when(function() use (&$calledSuccess) {\n            $calledSuccess = true;\n        }, function() use (&$calledFailure) {\n            $calledFailure = true;\n        });\n        $this->assertFalse($calledSuccess);\n        $this->assertFalse($calledFailure);\n\n        $promise->fail(true);\n        $this->assertFalse($calledSuccess, 'Not successful');\n        $this->assertTrue($calledFailure, 'Failed!');\n\n        try {\n            $promise->succeed(true);\n            $this->fail('No exception raised');\n        } catch (\\BadMethodCallException $e) {\n            $this->assertFalse($calledSuccess);\n        }\n    }\n\n\n    public function testSucceed() {\n        $promise = new Deferred;\n        $calledSuccess = false;\n        $calledFailure = false;\n        $promise->when(function() use (&$calledSuccess) {\n            $calledSuccess = true;\n        }, function() use (&$calledFailure) {\n            $calledFailure = true;\n        });\n        $this->assertFalse($calledSuccess);\n        $this->assertFalse($calledFailure);\n\n        $promise->succeed(true);\n        $this->assertTrue($calledSuccess, 'Success was not called');\n        $this->assertFalse($calledFailure, 'Fail was called');\n\n        try {\n            $promise->fail(true);\n            $this->fail('No exception raised');\n        } catch (\\BadMethodCallException $e) {\n            $this->assertFalse($calledFailure, 'Fail is true!');\n        }\n    }\n\n    public function testAlreadyResolvedPromise() {\n        $promise = new Deferred();\n        $promise->succeed(true);\n        $calledSuccess = false;\n        $calledFailure = false;\n        $promise->when(function() use (&$calledSuccess) {\n            $calledSuccess = true;\n        }, function() use (&$calledFailure) {\n            $calledFailure = true;\n        });\n        $this->assertTrue($calledSuccess);\n        $this->assertFalse($calledFailure);\n       \n    }\n\n    public function testPromiseValues() {\n        $promise = new Deferred();\n        $receivedValue = null;\n        $promise->when(function($a) {\n            return $a + 1;\n        })->when(function($b) use (&$receivedValue) {\n            $receivedValue = $b;\n        });\n        $this->assertEquals(null, $receivedValue);\n        $promise->succeed(2);\n        $this->assertEquals(3, $receivedValue);\n    }\n\n}"
  }
]