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

Matt Machuga

July 26, 2017
Tweet

More Decks by Matt Machuga

Other Decks in Programming

Transcript

  1. Maybes and Functors
    Matthew Machuga

    Laracon 2017 Science Fair

    View full-size slide

  2. You know what’s
    awful?

    View full-size slide

  3. 1 billion dollar
    mistake

    View full-size slide

  4. We’ll get to that in a
    bit

    View full-size slide

  5. You know what’s
    awesome?

    View full-size slide

  6. Collection Operations

    View full-size slide

  7. 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 full-size slide

  8. Interesting Properties
    Hidden Here

    View full-size slide

  9. Or: Functor Laws

    View full-size slide

  10. 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 full-size slide

  11. Collection Operations
    are Pipelines

    View full-size slide

  12. But what about
    single values?

    View full-size slide

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

    View full-size slide

  14. Let’s create a type

    View full-size slide

  15. 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 full-size slide

  16. We’ll call it Box

    View full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

  20. Cool. What about
    null?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    2. The element was found and is null

    View full-size slide

  28. What does null mean?
    1. Presence

    2. Value

    View full-size slide

  29. PHP Conventions
    Didn’t find something?

    • Return false

    • Throw exception

    View full-size slide

  30. How to represent
    both?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. But is this better than
    null?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  39. 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 full-size slide

  40. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  44. 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 full-size slide

  45. 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 full-size slide

  46. 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 full-size slide

  47. Will this improve
    CRUD?

    View full-size slide

  48. Eloquent::findMaybe($id)

    View full-size slide

  49. Will this improve complex
    data transformations?

    View full-size slide

  50. Composable &
    Reusable functions

    View full-size slide

  51. 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 full-size slide