Full Code of ircmaxell/monad-php for AI

master c9a4970eb36d cached
15 files
15.2 KB
4.3k tokens
45 symbols
1 requests
Download .txt
Repository: ircmaxell/monad-php
Branch: master
Commit: c9a4970eb36d
Files: 15
Total size: 15.2 KB

Directory structure:
gitextract_hl151y4g/

├── .gitignore
├── README.md
├── composer.json
├── lib/
│   └── MonadPHP/
│       ├── Chain.php
│       ├── Deferred.php
│       ├── Identity.php
│       ├── ListMonad.php
│       ├── Maybe.php
│       ├── Monad.php
│       └── Promise.php
├── phpunit.xml.dist
└── test/
    └── MonadPHP/
        ├── IdentityTest.php
        ├── ListMonadTest.php
        ├── MaybeTest.php
        └── PromiseTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
vendor

================================================
FILE: README.md
================================================
MonadPHP
========

This is a basic Monad library for PHP.

Usage
=====

Values are "wrapped" in the monad via either the constructor: `new MonadPHP\Identity($value)` or the `unit()` method on an existing instance: `$monad->unit($value);`

Functions can be called on the wrapped value using `bind()`:

    use MonadPHP\Identity;
    $monad = new Identity(1);
    $monad->bind(function($value) { var_dump($value); });
    // Prints int(1)

All calls to bind return a new monad instance wrapping the return value of the function.

     use MonadPHP\Identity;
    $monad = new Identity(1);
    $monad->bind(function($value) {
            return 2 * $value;
        })->bind(function($value) { 
            var_dump($value); 
        });
    // Prints int(2)

Additionally, "extracting" the raw value is supported as well (since this is PHP and not a pure functional language)...

    use MonadPHP\Identity;
    $monad = new Identity(1);
    var_dump($monad->extract());
    // Prints int(1)

Maybe Monad
===========

One 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`.

    use MonadPHP\Maybe;
    $monad = new Maybe(1);
    $monad->bind(function($value) { var_dump($value); });
    // prints int(1)

    $monad = new Maybe(null);
    $monad->bind(function($value) { var_dump($value); });
    // prints nothing (callback never called)...

The included Chain monad does the same thing, but providing a short-cut implementation for objects:

    use MonadPHP\Chain;
    $monad = new Chain($someChainableObject);
    $obj = $monad->call1()->call2()->nonExistantMethod()->call4()->extract();
    var_dump($obj);
    // null

This can prevent errors when used with chaining...

List Monad
==========

This abstracts away the concept of a list of items (an array):

    use MonadPHP\ListMonad;
    $monad = new ListMonad(array(1, 2, 3, 4));
    $doubled = $monad->bind(function($value) { return 2 * $value; });
    var_dump($doubled->extract());
    // Prints array(2, 4, 6, 8)

Note that the passed in function gets called once per value, so it only ever deals with a single element, never the entire array...

It 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...

Composition
===========

These Monads can be composed together to do some really useful things:

    use MonadPHP\ListMonad;
    use MonadPHP\Maybe;

    $monad = new ListMonad(array(1, 2, 3, null, 4));
    $newMonad = $monad->bind(function($value) { return new Maybe($value); });
    $doubled = $newMonad->bind(function($value) { return 2 * $value; });
    var_dump($doubled->extract());
    // Prints array(2, 4, 6, null, 8)

Or, what if you want to deal with multi-dimensional arrays?

    use MonadPHP\ListMonad;
    $monad = new ListMonad(array(array(1, 2), array(3, 4), array(5, 6)));
    $newMonad = $monad->bind(function($value) { return new ListMonad($value); });
    $doubled = $newMonad->bind(function($value) { return 2 * $value; });
    var_dump($doubled->extract());
    // Prints array(array(2, 4), array(6, 8), array(10, 12))

There also exist helper constants on each of the monads to get a callback to the `unit` method:

    $newMonad = $monad->bind(Maybe::unit);
    // Does the same thing as above 

Real World Example
==================

Imagine that you want to traverse a multi-dimensional array to create a list of values of a particular sub-key. For example:

    $posts = array(
        array("title" => "foo", "author" => array("name" => "Bob", "email" => "bob@example.com")),
        array("title" => "bar", "author" => array("name" => "Tom", "email" => "tom@example.com")),
        array("title" => "baz"),
        array("title" => "biz", "author" => array("name" => "Mark", "email" => "mark@example.com")),
    );
    
What 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.

First, we define a function to return a particular index of an array:

    function index($key) {
        return function($array) use ($key) {
            return isset($array[$key]) ? $array[$key] : null;
        };
    }
    
Basically, 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.

    $postMonad = new MonadPHP\ListMonad($posts);
    $names = $postMonad
        ->bind(MonadPHP\Maybe::unit)
        ->bind(index("author"))
        ->bind(index("name"))
        ->extract();
        
Follow through and see what happens!


================================================
FILE: composer.json
================================================
{
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-0": {
            "MonadPHP": "lib"
        }
    }
}

================================================
FILE: lib/MonadPHP/Chain.php
================================================
<?php

namespace MonadPHP;

class Chain extends Maybe {

    public function __set($name, $value) {
        return $this->bind(function($obj) use($name, $value) {
            $obj->$name = $value;
            return $value;
        });
    }

    public function __get($name) {
        return $this->bind(function($obj) use($name) {
            if (isset($obj->$name)) {
                return $obj->$name;
            }
            return null;
        });
    }

    public function __call($name, array $args = array()) {
        return $this->bind(function($obj) use ($name, $args) {
            if (is_callable(array($obj, $name))) {
                return call_user_func_array(array($obj, $name), $args);
            }
            return null;
        });
    }

}

================================================
FILE: lib/MonadPHP/Deferred.php
================================================
<?php

namespace MonadPHP;

class Deferred extends Promise {

    public function succeed($value) {
        $this->resolve(true, $value);
    }

    public function fail($value) {
        $this->resolve(false, $value);
    }

}

================================================
FILE: lib/MonadPHP/Identity.php
================================================
<?php

namespace MonadPHP;

class Identity extends Monad {

    const unit = "MonadPHP\Identity::unit";

}


================================================
FILE: lib/MonadPHP/ListMonad.php
================================================
<?php

namespace MonadPHP;

class ListMonad extends Monad {

    const unit = "MonadPHP\ListMonad::unit";

    public function __construct($value) {
        if (!is_array($value) && !$value instanceof \Traversable) {
            throw new \InvalidArgumentException('Must be traversable');
        }
        return parent::__construct($value);
    }

    public function bind($function, array $args = array()) {
        $result = array();
        foreach ($this->value as $value) {
            $result[] = $this->runCallback($function, $value, $args);
        }
        return $this::unit($result);
    }

    public function extract() {
        $ret = array();
        foreach ($this->value as $value) {
            if ($value instanceof Monad) {
                $ret[] = $value->extract();
            } else {
                $ret[] = $value;
            }
        }
        return $ret;
    }

}


================================================
FILE: lib/MonadPHP/Maybe.php
================================================
<?php

namespace MonadPHP;

class Maybe extends Monad {

    const unit = "MonadPHP\Maybe::unit";

    public function bind($function){
        if (!is_null($this->value)) {
            return parent::bind($function);
        }
        return $this::unit(null);
    }

}


================================================
FILE: lib/MonadPHP/Monad.php
================================================
<?php

namespace MonadPHP;

abstract class Monad {

    protected $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public static function unit($value) {
        if ($value instanceof static) {
            return $value;
        }
        return new static($value);
    }

    public function bind($function, array $args = array()) {
        return $this::unit($this->runCallback($function, $this->value, $args));
    }

    public function extract() {
        if ($this->value instanceof self) {
            return $this->value->extract();
        }
        return $this->value;
    }

    protected function runCallback($function, $value, array $args = array()) {
        if ($value instanceof self) {
            return $value->bind($function, $args);
        }
        array_unshift($args, $value);
        return call_user_func_array($function, $args);
    }

}


================================================
FILE: lib/MonadPHP/Promise.php
================================================
<?php

namespace MonadPHP;

class Promise extends Monad {

    const unit = "MonadPHP\Promise::unit";

    protected $isResolved = false;
    protected $succeed = null;

    protected $children = array();
    protected $success;
    protected $failure;

    public function __construct($success = null, $failure = null) {
        $this->success = $success;
        $this->failure = $failure;
    }

    public static function unit($success = null, $failure = null) {
        return new Promise($success, $failure); 
    }

    public function bind($success, $failure) {
        $obj = $this->unit($success, $failure);
        if ($this->isResolved) {
            $obj->resolve($this->succeed, $this->value);
        } else {
            $this->children[] = $obj;
        }
        return $obj;
    }

    protected function resolve($status, $value) {
        if ($this->isResolved) {
            throw new \BadMethodCallException('Promise already resolved');
        }
        $this->value = $value;
        $this->isResolved = true;
        $this->succeed = $status;
        $callback = $status ? $this->success : $this->failure;
        if ($callback) {
            $this->value = call_user_func($callback, $value);
        }
        foreach ($this->children as $child) {
            $child->resolve($status, $this->value);
        }
    }

    public function when($success = null, $failure = null) {
        return $this->bind($success, $failure);
    }
}


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="true"
         backupStaticAttributes="true"
         bootstrap="./vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         forceCoversAnnotation="false"
         mapTestClassNameToCoveredClassName="false"
         processIsolation="false"
         stopOnError="false"
         stopOnFailure="false"
         stopOnIncomplete="false"
         stopOnSkipped="false"
         testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
         strict="true"
         verbose="false">
    <testsuites>
        <testsuite name="Unit">
            <directory>./test/</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory suffix=".php">./lib/</directory>
        </whitelist>
    </filter>
</phpunit>

================================================
FILE: test/MonadPHP/IdentityTest.php
================================================
<?php

namespace MonadPHP;

class IdentityTest extends \PHPUnit_Framework_TestCase {

    public function testBind() {
        $monad = new Identity(1);
        $this->assertEquals($monad->unit(1), $monad->bind('intval'));
    }

    public function testBindUnit() {
        $monad = new Identity(1);
        $this->assertEquals($monad, $monad->bind($monad::unit));
    }

    public function testExtract() {
        $monad = new Identity(new Maybe(1));
        $this->assertEquals(1, $monad->extract());
    }

}


================================================
FILE: test/MonadPHP/ListMonadTest.php
================================================
<?php

namespace MonadPHP;

class ListMonadTest extends \PHPUnit_Framework_TestCase {

    public function testUnitFailure() {
        $this->setExpectedException('InvalidArgumentException');
        $monad = new ListMonad(123);
    }

    public function testBindEmpty() {
        $monad = new ListMonad(array());
        $called = 0;
        $monad->bind(function($value) use (&$called) { $called++; return $value; });
        $this->assertEquals(0, $called);
    }

    public function testBindNotEmpty() {
        $monad = new ListMonad(array(1, 2, 3));
        $called = 0;
        $new = $monad->bind(function($value) use (&$called) { $called++; return $value; });
        $this->assertEquals(3, $called);
        $this->assertEquals($monad, $new);
        $this->assertEquals(array(1, 2, 3), $new->extract());
    }

    public function testComposedListMonad() {
        $monad = new ListMonad(array(1, 2, 3, null, 4));
        $newMonad = $monad->bind(function ($v) { return new Maybe($v); });
        $doubled = $newMonad->bind(function ($v) { return $v * 2; });
        $this->assertEquals(array(2, 4, 6, null, 8), $doubled->extract());
    }


}

================================================
FILE: test/MonadPHP/MaybeTest.php
================================================
<?php

namespace MonadPHP;

class MaybeTest extends \PHPUnit_Framework_TestCase {

    public function testBind() {
        $monad = new Maybe(1);
        $this->assertEquals(new Maybe("1"), $monad->bind("strval"));
        
        $monad2 = $monad->unit(null);
        $called = false;
        $func = function($a) use (&$called) {
            $called = true;
        };
        $this->assertEquals(new Maybe(null), $monad2->bind($func));
        $this->assertFalse($called);
    }

    public function testUnit() {
        $monad = new Maybe(1);
        $this->assertEquals($monad, $monad->unit($monad));
    }

    public function testExtract() {
        $monad = new Maybe(1);
        $this->assertEquals(1, $monad->extract());
    }

    public function testSelfUnit() {
        $monad = new Maybe(1);
        $m2 = $monad->unit($monad);
        $m3 = $monad->unit($m2);
        $this->assertEquals($monad, $m3);
    }

}

================================================
FILE: test/MonadPHP/PromiseTest.php
================================================
<?php

namespace MonadPHP;

class PromiseTest extends \PHPUnit_Framework_TestCase {

    public function testFail() {
        $promise = new Deferred;
        $calledSuccess = false;
        $calledFailure = false;
        $promise->when(function() use (&$calledSuccess) {
            $calledSuccess = true;
        }, function() use (&$calledFailure) {
            $calledFailure = true;
        });
        $this->assertFalse($calledSuccess);
        $this->assertFalse($calledFailure);

        $promise->fail(true);
        $this->assertFalse($calledSuccess, 'Not successful');
        $this->assertTrue($calledFailure, 'Failed!');

        try {
            $promise->succeed(true);
            $this->fail('No exception raised');
        } catch (\BadMethodCallException $e) {
            $this->assertFalse($calledSuccess);
        }
    }


    public function testSucceed() {
        $promise = new Deferred;
        $calledSuccess = false;
        $calledFailure = false;
        $promise->when(function() use (&$calledSuccess) {
            $calledSuccess = true;
        }, function() use (&$calledFailure) {
            $calledFailure = true;
        });
        $this->assertFalse($calledSuccess);
        $this->assertFalse($calledFailure);

        $promise->succeed(true);
        $this->assertTrue($calledSuccess, 'Success was not called');
        $this->assertFalse($calledFailure, 'Fail was called');

        try {
            $promise->fail(true);
            $this->fail('No exception raised');
        } catch (\BadMethodCallException $e) {
            $this->assertFalse($calledFailure, 'Fail is true!');
        }
    }

    public function testAlreadyResolvedPromise() {
        $promise = new Deferred();
        $promise->succeed(true);
        $calledSuccess = false;
        $calledFailure = false;
        $promise->when(function() use (&$calledSuccess) {
            $calledSuccess = true;
        }, function() use (&$calledFailure) {
            $calledFailure = true;
        });
        $this->assertTrue($calledSuccess);
        $this->assertFalse($calledFailure);
       
    }

    public function testPromiseValues() {
        $promise = new Deferred();
        $receivedValue = null;
        $promise->when(function($a) {
            return $a + 1;
        })->when(function($b) use (&$receivedValue) {
            $receivedValue = $b;
        });
        $this->assertEquals(null, $receivedValue);
        $promise->succeed(2);
        $this->assertEquals(3, $receivedValue);
    }

}
Download .txt
gitextract_hl151y4g/

├── .gitignore
├── README.md
├── composer.json
├── lib/
│   └── MonadPHP/
│       ├── Chain.php
│       ├── Deferred.php
│       ├── Identity.php
│       ├── ListMonad.php
│       ├── Maybe.php
│       ├── Monad.php
│       └── Promise.php
├── phpunit.xml.dist
└── test/
    └── MonadPHP/
        ├── IdentityTest.php
        ├── ListMonadTest.php
        ├── MaybeTest.php
        └── PromiseTest.php
Download .txt
SYMBOL INDEX (45 symbols across 11 files)

FILE: lib/MonadPHP/Chain.php
  class Chain (line 5) | class Chain extends Maybe {
    method __set (line 7) | public function __set($name, $value) {
    method __get (line 14) | public function __get($name) {
    method __call (line 23) | public function __call($name, array $args = array()) {

FILE: lib/MonadPHP/Deferred.php
  class Deferred (line 5) | class Deferred extends Promise {
    method succeed (line 7) | public function succeed($value) {
    method fail (line 11) | public function fail($value) {

FILE: lib/MonadPHP/Identity.php
  class Identity (line 5) | class Identity extends Monad {

FILE: lib/MonadPHP/ListMonad.php
  class ListMonad (line 5) | class ListMonad extends Monad {
    method __construct (line 9) | public function __construct($value) {
    method bind (line 16) | public function bind($function, array $args = array()) {
    method extract (line 24) | public function extract() {

FILE: lib/MonadPHP/Maybe.php
  class Maybe (line 5) | class Maybe extends Monad {
    method bind (line 9) | public function bind($function){

FILE: lib/MonadPHP/Monad.php
  class Monad (line 5) | abstract class Monad {
    method __construct (line 9) | public function __construct($value) {
    method unit (line 13) | public static function unit($value) {
    method bind (line 20) | public function bind($function, array $args = array()) {
    method extract (line 24) | public function extract() {
    method runCallback (line 31) | protected function runCallback($function, $value, array $args = array(...

FILE: lib/MonadPHP/Promise.php
  class Promise (line 5) | class Promise extends Monad {
    method __construct (line 16) | public function __construct($success = null, $failure = null) {
    method unit (line 21) | public static function unit($success = null, $failure = null) {
    method bind (line 25) | public function bind($success, $failure) {
    method resolve (line 35) | protected function resolve($status, $value) {
    method when (line 51) | public function when($success = null, $failure = null) {

FILE: test/MonadPHP/IdentityTest.php
  class IdentityTest (line 5) | class IdentityTest extends \PHPUnit_Framework_TestCase {
    method testBind (line 7) | public function testBind() {
    method testBindUnit (line 12) | public function testBindUnit() {
    method testExtract (line 17) | public function testExtract() {

FILE: test/MonadPHP/ListMonadTest.php
  class ListMonadTest (line 5) | class ListMonadTest extends \PHPUnit_Framework_TestCase {
    method testUnitFailure (line 7) | public function testUnitFailure() {
    method testBindEmpty (line 12) | public function testBindEmpty() {
    method testBindNotEmpty (line 19) | public function testBindNotEmpty() {
    method testComposedListMonad (line 28) | public function testComposedListMonad() {

FILE: test/MonadPHP/MaybeTest.php
  class MaybeTest (line 5) | class MaybeTest extends \PHPUnit_Framework_TestCase {
    method testBind (line 7) | public function testBind() {
    method testUnit (line 20) | public function testUnit() {
    method testExtract (line 25) | public function testExtract() {
    method testSelfUnit (line 30) | public function testSelfUnit() {

FILE: test/MonadPHP/PromiseTest.php
  class PromiseTest (line 5) | class PromiseTest extends \PHPUnit_Framework_TestCase {
    method testFail (line 7) | public function testFail() {
    method testSucceed (line 32) | public function testSucceed() {
    method testAlreadyResolvedPromise (line 56) | public function testAlreadyResolvedPromise() {
    method testPromiseValues (line 71) | public function testPromiseValues() {
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (17K chars).
[
  {
    "path": ".gitignore",
    "chars": 6,
    "preview": "vendor"
  },
  {
    "path": "README.md",
    "chars": 4747,
    "preview": "MonadPHP\n========\n\nThis is a basic Monad library for PHP.\n\nUsage\n=====\n\nValues are \"wrapped\" in the monad via either the"
  },
  {
    "path": "composer.json",
    "chars": 135,
    "preview": "{\n    \"require\": {\n        \"php\": \">=5.3.0\"\n    },\n    \"autoload\": {\n        \"psr-0\": {\n            \"MonadPHP\": \"lib\"\n  "
  },
  {
    "path": "lib/MonadPHP/Chain.php",
    "chars": 769,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass Chain extends Maybe {\n\n    public function __set($name, $value) {\n        return $this"
  },
  {
    "path": "lib/MonadPHP/Deferred.php",
    "chars": 227,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass Deferred extends Promise {\n\n    public function succeed($value) {\n        $this->resol"
  },
  {
    "path": "lib/MonadPHP/Identity.php",
    "chars": 107,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass Identity extends Monad {\n\n    const unit = \"MonadPHP\\Identity::unit\";\n\n}\n"
  },
  {
    "path": "lib/MonadPHP/ListMonad.php",
    "chars": 899,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass ListMonad extends Monad {\n\n    const unit = \"MonadPHP\\ListMonad::unit\";\n\n    public fu"
  },
  {
    "path": "lib/MonadPHP/Maybe.php",
    "chars": 271,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass Maybe extends Monad {\n\n    const unit = \"MonadPHP\\Maybe::unit\";\n\n    public function b"
  },
  {
    "path": "lib/MonadPHP/Monad.php",
    "chars": 910,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nabstract class Monad {\n\n    protected $value;\n\n    public function __construct($value) {\n   "
  },
  {
    "path": "lib/MonadPHP/Promise.php",
    "chars": 1460,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass Promise extends Monad {\n\n    const unit = \"MonadPHP\\Promise::unit\";\n\n    protected $is"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 938,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit backupGlobals=\"true\"\n         backupStaticAttributes=\"true\"\n         boo"
  },
  {
    "path": "test/MonadPHP/IdentityTest.php",
    "chars": 514,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass IdentityTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testBind() {\n "
  },
  {
    "path": "test/MonadPHP/ListMonadTest.php",
    "chars": 1156,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass ListMonadTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testUnitFailu"
  },
  {
    "path": "test/MonadPHP/MaybeTest.php",
    "chars": 927,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass MaybeTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testBind() {\n    "
  },
  {
    "path": "test/MonadPHP/PromiseTest.php",
    "chars": 2528,
    "preview": "<?php\n\nnamespace MonadPHP;\n\nclass PromiseTest extends \\PHPUnit_Framework_TestCase {\n\n    public function testFail() {\n  "
  }
]

About this extraction

This page contains the full source code of the ircmaxell/monad-php GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (15.2 KB), approximately 4.3k tokens, and a symbol index with 45 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!