Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ircmaxell/monad-php

A simple Monad library for PHP
https://github.com/ircmaxell/monad-php

Last synced: about 2 months ago
JSON representation

A simple Monad library for PHP

Awesome Lists containing this project

README

        

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" => "[email protected]")),
array("title" => "bar", "author" => array("name" => "Tom", "email" => "[email protected]")),
array("title" => "baz"),
array("title" => "biz", "author" => array("name" => "Mark", "email" => "[email protected]")),
);

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!