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

Maybes and Functors

Maybes and Functors

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

Matthew Machuga

July 26, 2017
Tweet

More Decks by Matthew Machuga

Other Decks in Programming

Transcript

  1. Maybes and Functors
    Matthew Machuga

    Laracon 2017 Science Fair

    View Slide

  2. You know what’s
    awful?

    View Slide

  3. null

    View Slide

  4. 1 billion dollar
    mistake

    View Slide

  5. We’ll get to that in a
    bit

    View Slide

  6. You know what’s
    awesome?

    View Slide

  7. Collection Operations

    View Slide

  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”]

    View Slide

  9. Interesting Properties
    Hidden Here

    View Slide

  10. Or: Functor Laws

    View Slide

  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)

    View Slide

  12. Collection Operations
    are Pipelines

    View Slide

  13. But what about
    single values?

    View Slide

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

    View Slide

  15. Let’s create a type

    View Slide

  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

    View Slide

  17. We’ll call it Box

    View Slide

  18. Box
    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;
    }
    }

    View Slide

  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”]

    View Slide

  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”

    View Slide

  21. Cool. What about
    null?

    View Slide

  22. Collections

    View Slide

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

    View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. View Slide

  28. You start getting this
    $value = collect([somethingThatMightBeNull()])
    ->map(function($v) {
    if ($v && method_exists($v, ‘byebye’)) {
    return $v->byebye();
    }
    });

    View Slide

  29. You can technically do this..
    $value = collect([somethingThatMightBeNull()])
    ->filter(function($el) {
    return !!$el;
    })
    ->map(function($v) {
    return $v->byebye();
    });

    View Slide

  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)

    View Slide

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

    View Slide

  32. View Slide

  33. null

    View Slide

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

    View Slide

  35. What does null mean?
    1. The element was not found

    2. The element was found and is null

    View Slide

  36. What does null mean?
    1. Presence

    2. Value

    View Slide

  37. PHP Conventions
    Didn’t find something?

    • Return false

    • Throw exception

    View Slide

  38. How to represent
    both?

    View Slide

  39. Maybe

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. But is this better than
    null?

    View Slide

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

    View Slide

  47. Don’t do this
    Without pattern matching ¯\_(ツ)_/¯

    View Slide

  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
    });
    }

    View Slide

  49. View Slide

  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)))

    View Slide

  51. map
    public function map(callable $f) : Functor
    {
    return new static($f($this->value));
    }

    View Slide

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

    View Slide

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

    View Slide

  54. View Slide

  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)

    View Slide

  56. You don’t like closures?
    class ConvertToTruth {
    public function convert($text) {
    return str_replace('the cloud',
    “someone else's computer", $text);
    }
    public function __invoke($text) {
    return $this->convert($text);
    }
    }

    View Slide

  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

    View Slide

  58. Will this improve
    CRUD?

    View Slide

  59. Eloquent::findMaybe($id)

    View Slide

  60. Will this improve complex
    data transformations?

    View Slide

  61. Composable &
    Reusable functions

    View Slide

  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/

    View Slide