Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Monads In PHP → php[tek]

Monads In PHP → php[tek]

Many developers get lost in the hype of object oriented design. They miss out on how expressive and succinct their code could be if they tried functional programming.

Take Monads, for instance. Many developers haven't even heard the name, much less are able to describe what Monads are and how they can be useful in every-day code.

In this talk, we'll gain a clear and simple understanding of what Monads are, and how they can help us to refactor our code to be clear and concise.

Christopher Pitt

May 27, 2016
Tweet

More Decks by Christopher Pitt

Other Decks in Technology

Transcript

  1. RULES ▸ $stack->push($value)->top() == $value ▸ $stack->push($value)->pop() == $stack ▸

    $stack->push($value)->isEmpty() == false ▸ (new Stack)->isEmpty() == true
  2. public function push($value) { $clone = clone $this; array_push($clone->values, $value);

    return $clone; } public function pop() { $clone = clone $this; array_pop($clone->values); return $clone; }
  3. public function top() { $count = count($this->values); if ($count >

    0) { return $this->values[$count - 1]; } return null; } public function isEmpty() { return count($this->values) === 0; } }
  4. class LinkedListStack { private $head; private $tail; public function __construct($head

    = null, $tail = null) { $this->head = $head; $this->tail = $tail; }
  5. public function push($value) { return new static($value, clone $this); }

    public function pop() { return $this->tail; }
  6. trait HasSizeMethod { public function size() { if ($this->isEmpty()) {

    return 0; } return $this->pop()->size() + 1; } abstract public function isEmpty(); abstract public function pop(); }
  7. $stack1 = new ArrayStack(); $stack1->push("one")->push("two")->size(); // 2 $stack2 = new

    LinkedListStack(); $stack2->push("one")->push("two")->size(); // 2
  8. public function each(callable $func) { $interval = 60 * 60;

    $next = $this->start + interval; while ($next <= $this->end) { $func($next); $next += interval; } } }
  9. trait HasSelectMethod { public function select(callable $func) { $values =

    []; $this->each(function($value) use ($func, &$values) { if ($func($value)) { $values[] = $value; } }); return $values; } public abstract function each(callable $func); }
  10. function odd($value) { return $value % 2 == 0; }

    $collection1 = new RangeCollection(range(5, 10)); $collection1->select("odd"); // [5, 7, 9] $collection2 = new HourIntervalCollection( strtotime("+0 hours"), strtotime("+5 hours") ); $collection2->select("odd"); // [±1 hours, ±3 hours, ±5 hours]
  11. class Store { public $manager; } class Manager { public

    $address; } class Address { public $city; }
  12. $address = new Address(); $address->city = "St. Louis"; $manager =

    new Manager(); $manager->address = $address; $store = new Store(); $store->manager = $manager;
  13. function getManagerCityForStore($store) { if ($store !== null && $store->manager !==

    null) { $manager = $store->manager; if ($manager !== null && $manager->address !== null) { $address = $manager->address; if ($address !== null && $address->city !== null) { return $address->city } } } }
  14. function getManagerCityForStore($store) { if ($store !== null && $store->manager !==

    null) { $manager = $store->manager; } if ($manager !== null && $manager->address !== null) { $address = $manager->address; } if ($address !== null && $address->city !== null) { return $address->city } }
  15. trait HasTryMethod { public function try($property) { if ($this->$property !==

    null) { return $this->$property; } return null; } }
  16. class Store { use HasTryMethod; public $manager; } class Manager

    { use HasTryMethod; public $address; } class Address { use HasTryMethod; public $city; }
  17. function getManagerCityForStore($store) { if ($store !== null) { $manager =

    $store->try("manager"); $address = $manager->try("address"); $city = $address->try("city"); return $city; } }
  18. class Maybe { private $value; public function __construct($value) { if

    (is_object($value) && $value instanceof static) { $value = $value->value(); } $this->value = $value; } public static function create(...$params) { return new static(...$params) }
  19. public function try($property) { if ($this->value !== null) { return

    $this->value->$property; } return null; } public function value() { return $this->value; } }
  20. function getManagerCityForStore($store) { $store = Maybe::create($store); $manager = Maybe::create($store->try("manager")); $address

    = Maybe::create($manager->try("address")); $city = Maybe::create($address->try("city")); return $city; }
  21. public function try($property) { if ($this->value !== null) { return

    static::create($this->value->$property); } return static::create(null); }
  22. public function try(callable $func) { if ($this->value !== null) {

    return static::create($func($this->value)); } return static::create(null); }
  23. function getManagerCityForStore($store) { return Maybe::create($store) ->try(function($store) { return $store->manager; })

    ->try(function($manager) { return $manager->address; }) ->try(function($address) { return $address->city; }) ->value(); }
  24. public function then(callable $func) { if ($this->value !== null) {

    return static::create($func($this->value)); } return static::create(null); } public function __call($method) { return $this->then(function($value) use ($method) { return $value->$method; }); } public function __get($property) { return $this->$property(); }
  25. class Blog { public $categories; } class Category { public

    $posts; } class Post { public $comments; }
  26. $post1 = new Post(); $post1->comments = ["frist!", "you suck"]; $post2

    = new Post(); $post2->comments = ["inb4comments", "have this moneys"]; $category1 = new Category(); $category1->posts = [$post2]; $category2 = new Category(); $category2->posts = [$post1]; $blog = new Blog(); $blog->categories = [$category1, $category2];
  27. function map($iterable, callable $func) { foreach ($iterable as $value) {

    yield $func($value); } } function flatMap($iterable, callable $func) { foreach (map($iterable, $func) as $outer) { foreach ($outer as $inner) { yield $inner; } } }
  28. function getCommentWordsForBlog($blog) { return flatMap($blog->categories, function($category) { return flatMap($category->posts, function($post)

    { return flatMap($post->comments, function($comment) { return explode(" ", $comment); }); }); }); }
  29. class Many { private $values; public function __construct($values) { if

    (is_object($values) && $values instanceof static) { $values = $values->value(); } $this->values = $values; } public static function create(...$params) { return new static(...$params); }
  30. function getCommentWordsForBlog($blog) { return Many::create($blog->categories) ->then(function($category) { return $category->posts; })

    ->then(function($post) { return $post->comments; }) ->then(function($comment) { return explode(" ", $comment); }) ->value(); }
  31. public function __call($method) { return $this->then(function($value) use ($method) { return

    $value->$method; }); } public function __get($property) { return $this->$property(); }
  32. class Maybe { public function then(callable $func) { if ($this->value

    !== null) { return static::create($func($this->value)); } return static::create(null); } ... } class Many { public function then(callable $func) { return static::create( flatMap($this->values, $func) ); } ... }
  33. class Maybe { public static function from($value) { return static::create($value);

    } ... } class Many { public static function from($value) { return static::create([$value]); } ... }
  34. RULES ▸ ::then(callable $func) calls a function zero or more

    times, at some point ▸ ::then(callable $func) returns an instance of the same monad ▸ ::from(callable $func) doesn't mess with the value
  35. $maybe = Maybe::create(["name" => "chris"]); upperCaseNameWithin($maybe); // "CHRIS" $many =

    Many::create([["name" => "chris"], ["name" => "cal"]]); upperCaseNameWithin($many); // ["CHRIS", "CAL"]