Pro Yearly is on sale from $80 to $50! »

Maybes and Functors

Maybes and Functors

A brief introductions to functors and the Maybe type, briefly touching on a tiny bit of Monadic properties

8f9f6a577da77a9add9cadbb90e66b75?s=128

Matthew Machuga

July 26, 2017
Tweet

Transcript

  1. Maybes and Functors Matthew Machuga Laracon 2017 Science Fair

  2. You know what’s awful?

  3. null

  4. 1 billion dollar mistake

  5. We’ll get to that in a bit

  6. You know what’s awesome?

  7. Collection Operations

  8. Contrived Ex 1: $users = collect([ new User([“id” => 1,

    “name” => “Rommie Bot”]), new User([“id” => 2, “name” => “Liona Bot”]) ]); $getInfo = function($user) { return $user->name; }; $toSlug = function($name) { return strtolower(str_replace(“ “, “-“, $name)); }; $slugifiedNames = $collection->map($getInfo)->map($toSlug); $slugifiedNames // => [“rommie-bot”, “liona-bot”]
  9. Interesting Properties Hidden Here

  10. Or: Functor Laws

  11. Interesting Properties / Functor Laws // map won’t run if

    the collection is empty $c->map(function($x) { return $x; }) == $c 
 $c->map($getInfo)->map($toSlug) == $c->map(function($user) { return $toSlug($getInfo($user)); }); // map(f).map(g) == map(g o f)
  12. Collection Operations are Pipelines

  13. But what about single values?

  14. You could do this.. $value = collect([$something]) ->map($doSomething) ->map($doSomethingElse) ->first();

  15. Let’s create a type

  16. Let's create a type • Follow the two rules •

    c.map(g).map(f) == c.map(g o f) • c.map(id) == c • Define a way to get to our raw value
  17. We’ll call it Box

  18. Box <?php class Box { protected $value; public static function

    of($value) { return new static($value); } public function __construct($value) { $this->value = $value; } public function map(callable $f) : Box { return new static($f($this->value)); } public function fold(callable $f = null) { return $f ? $f($this->value) : $this->value; } }
  19. Collection Example $users = collect([ new User([“id” => 1, “name”

    => “Rommie Bot”]), new User([“id” => 2, “name” => “Liona Bot”]) ]); $getInfo = function($user) { return $user->name; }; $toSlug = function($name) { return strtolower(str_replace(“ “, “-“, $name)); }; $slugifiedNames = $collection->map($getInfo)->map($toSlug); $slugifiedNames // => [“rommie-bot”, “liona-bot”]
  20. Box Example $box = Box::of(new User([“id” => 1, “name” =>

    “Liona Bot”])); $getInfo = function($user) { return $user->name; }; $toSlug = function($name) { return strtolower(str_replace(“ “, “-“, $name)); }; $slugifiedNameInBox = $box->map($getInfo)->map($toSlug); $slugifiedNameInBox // => Box(“liona-bot”) $slugifiedNameInBox->fold() // => “liona-bot”
  21. Cool. What about null?

  22. Collections

  23. What happens? $value = collect([null])->map(function($v) { return $v->byebye(); });

  24. None
  25. None
  26. None
  27. None
  28. You start getting this $value = collect([somethingThatMightBeNull()]) ->map(function($v) { if

    ($v && method_exists($v, ‘byebye’)) { return $v->byebye(); } });
  29. You can technically do this.. $value = collect([somethingThatMightBeNull()]) ->filter(function($el) {

    return !!$el; }) ->map(function($v) { return $v->byebye(); });
  30. Or possibly this.. $value = collect([somethingThatMightBeNull()]) ->filter(function($el) { return !!$el;

    }) ->map(function($v) { return $v->byebye(); }); // map(f).map(g) == map(g o f)
  31. Okay how about box? $value = Box::of(null)->map(function($v) { return $v->byebye();

    });
  32. None
  33. null

  34. Null $collection = collect([1, null]); $value = $collection->find(function($el) { return

    $el === 42; }); $value // => null
  35. What does null mean? 1. The element was not found

    2. The element was found and is null
  36. What does null mean? 1. Presence 2. Value

  37. PHP Conventions Didn’t find something? • Return false • Throw

    exception
  38. How to represent both?

  39. Maybe

  40. Maybe • Just(value) [Some(value)] • Nothing [None]

  41. Contrived Ex 1: $collection = collect([1, null]); $value = $collection->find(function($el)

    { return $el === 42; }); $value // => null
  42. Contrived Ex 1 Reprise: $collection = collect([ Maybe::just(1), Maybe::nothing() ]);

    $value = $collection->find(function($el) { return $el->fold() === 42; }); $value // => Nothing
  43. Contrived Ex 1 Reprise: $collection = collect([ Maybe::just(1), Maybe::nothing() ]);

    $value = $collection->find(function($el) { return $el->fold() === null; }); $value // => Just(null)
  44. Contrived Ex 1 Reprise: $collection = collect([ Maybe::just(1), Maybe::nothing() ]);

    $value = $collection->find(function($el) { return $el->fold() === 1; }); $value // => Just(1)
  45. But is this better than null?

  46. One way... $maybeUser = Eloquent::maybeFind(9001); if ($maybeUser->isNothing()) { // Do

    nothing } else { $something = $maybeUser->fold(); }
  47. Don’t do this Without pattern matching ¯\_(ツ)_/¯

  48. A better way... $maybeUser = Eloquent::maybeFind(9001); $something = $maybeUser->map(function($user) {

    return SomeObject::someTransformationLol($user); }); // Later in the app function process(Maybe $maybeData) { return $maybeData->fold(function($value) { // If you need to do a side-effect dependent on if null // Could finally check here }); }
  49. None
  50. Nested Types $value = Box::of(1)->map(function($id) { return Eloquent::maybeFind(9001); }); //

    Box(Just(User)) | Box(Nothing) $value->map(function($user) { // $user is a Just(User) return $user->profile; }); // Box(Just(Maybe(User)))
  51. map public function map(callable $f) : Functor { return new

    static($f($this->value)); }
  52. chain | flatMap public function chain(callable $f) : Monad {

    return $f($this->value); }
  53. chain | flatMap public function chain(callable $f) : Monad {

    return $f($this->value); }
  54. None
  55. Nested Types $value = Box::of(1)->chain(function($id) { return Eloquent::maybeFind(9001); }); //

    Just(User) | Nothing $value->chain(function($user) { return $user->profile; }); // Maybe(Profile)
  56. You don’t like closures? <?php class ConvertToTruth { public function

    convert($text) { return str_replace('the cloud', “someone else's computer", $text); } public function __invoke($text) { return $this->convert($text); } }
  57. You don’t like closures? collect(["My data lives in the cloud"])

    ->map(new ConvertToTruth) ->toArray() // [“My data lives in someone else’s machine”] // or Box::of(“My data lives in the cloud") ->map(new ConvertToTruth) ->fold() // My data lives in someone else’s machine
  58. Will this improve CRUD?

  59. Eloquent::findMaybe($id)

  60. Will this improve complex data transformations?

  61. Composable & Reusable functions

  62. Resources • Professor Frisby’s Mostly Adequate Guide to Functional Programming

    - Brian Lonsdorf (@drboolean) • https://drboolean.gitbooks.io/mostly-adequate-guide/content/ • https://egghead.io/courses/professor-frisby-introduces-composable- functional-javascript • Elm • http://elm-lang.org • Maybe Haskell - Pat Brisbin: • https://gumroad.com/l/maybe-haskell/