Go Mad For Monads

Warren Seymour

Lead Developer, radify.io

 

warren@radify.io

 

@woogoose

Pure Functions

function iAmPure($a, $b, $c) {
  /* ... */
  return $x;
}
  • Pass arguments by value
  • Only rely on argument values
    • No globals
    • No $this or static::
  • ​Easy to test/debug
  • Cache with memoization

Pure Functions

  • file_get_contents($filename)
  • mysql_query($query)
  • time()
  • rand()

Don't try to do anything practical...

Google: "What is a Monad?"

A monad is just a monoid in the category of endofunctors, what's the problem?

(>>=) :: (Monad m) => m a -> (a -> m b) -> m b  

"Go learn Haskell"

"Go learn Category Theory"

(M t) → (t → Mu) → (M u)

Crockford's Paradox

"Monads are cursed...

Once you fully understand and appreciate Monads...

You lose the ability to explain them to other people."

Douglas Crockford

The Loophole

  • I don't fully understand them
  • I will share what I've learned so far
  • You fill in the blanks
  • Monads are a loophole in the 'functional contract'
  • A way to write practical, pure functions

Fundamentals: unit & bind

function unit($value)

function bind($monad, function($value) { ... })
  • Three functions:
  • $value passed to function in bind === $value passed to unit
  • All return a new monad.
  • Function that returns a new thing === constructor
    • All Monads are constructors
    • Not all constructors are Monads

Axioms of Monads

1. bind(unit($value), $fn) === $fn($value);
2. bind($monad, unit) === $monad;
3. bind(bind($monad, $fnA), $fnB) ===

   bind($monad, function($value) {
       return bind($fnA($value), $fnB);
   });

Go-OO

  • Consider Monad a class
    • Antithetical to FP?
    • Typeclass?
  • Make unit a sugared constructor:
    • $monad = Monad::unit($value)
  • Make bind a class method:
    • ​$monad->bind($fn)

Axioms of class Monad

1. Monad::unit($value)->bind($fn) === $fn($value);
2. $monad->bind(Monad::unit) === $monad;
3. $monad->bind($fnA)->bind($fnB) ===

   $monad->bind(function($value) {
       return $fnA($value)->bind($fnB);
   });

Implementation

class Monad {
    protected $_value;

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

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

    public function bind($fn) {
        $result = $fn($this->_value);
        return static::unit($result);
    }
}

'Wrap' our value

Constructor sugar

Satisfy Axioms 1 and 3

Some tweaks...

/* ... */

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

        return new static($value);
    }

    public function value() {
        return $this->_value;
    }

/* ... */

Back to reality...

Satisfy Axiom 2

Live coding #1!

Monads Everywhere!

// Promises
$http.get('/api/products')
    .then(function(products) {
        return _.pluck(products, 'name');
    })
    .then(function(names) {
        $scope.names = names;
    });
// jQuery
$('div.foo')
    .css('background', 'red')
    .css('color', 'white')
    .html('hello world!');
// Lodash/Underscore:
_(value)
    .filter({category: 'book'})
    .sortBy('price')
    .reverse()
    .value();
// Doctrine QueryBuilder
$qb->select('u')
   ->from('User', 'u')
   ->where('u.id = ?1')
   ->orderBy('u.name', 'ASC')
   ->setParameter(1, 100);

"What is a musical instrument?"

  • Monads are a category of design patterns
  • They come in different flavours:
    • Identity    
    • Maybe
    • List
    • IO
    • Deferred aka Promise
    • ...more

Call me 'Maybe'

  • Sorry
  • Handle potential nulls
    • if ($x === null)
          return;
public function grandParentName() {
    return $this
        ->parent()
        ->parent()
        ->name;
}
public function grandParentName() {
    $parent = $this->parent();
    if ($parent === null) {
        return;
    }

    $grandParent = $parent->parent();
    if ($grandParent === null) {
        return;
    }

    return $grandParent->name;
}

Implementation

class Maybe extends Monad {

    public function bind($fn) {
        if ($this->_value === null) {
            return $this;
        }

        return parent::bind($fn);
    }
}

The last null check you'll ever write

... maybe

Live coding #2!

Maybe extend it

/* ... */

    public function call($name) {
        return $this->bind(function($value) use ($name) {
            return $value->name();
        });
    }

    public function prop($name) {
        return $this->bind(function($value) use ($name) {
            return $value->prop;
        });
    }

/* ... */

Method caller

Property getter

Conclusion

  • Monads are a category of design pattern
  • We've been using them for years!
  • Most common forms: simple implementation
  • Haskell and strong mathematical knowledge are not required
    • You should still give Haskell a try!
  • Go play!

Resources

  • Douglas Crockford: Gonads and Monads
    • https://www.youtube.com/watch?v=dkZFtimgAcM
  • Anthony Ferrara: Taking Monads to OOP PHP
    • http://blog.ircmaxell.com/2013/07/taking-monads-to-oop-php.html
  • Learn You a Haskell
    • http://learnyouahaskell.com/

Questions

Go Mad For Monads

By Warren Seymour

Go Mad For Monads

Monads in PHP. Why not?

  • 3,099