Matt Machuga
July 26, 2017
160

# Maybes and Functors

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

July 26, 2017

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

• Deﬁne a way to get to our raw value

17. We’ll call it Box

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

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. You start getting this
\$value = collect([somethingThatMightBeNull()])
->map(function(\$v) {
if (\$v && method_exists(\$v, ‘byebye’)) {
return \$v->byebye();
}
});

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

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

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

28. null

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

30. What does null mean?

2. The element was found and is null

31. What does null mean?
1. Presence

2. Value

32. PHP Conventions
Didn’t ﬁnd something?

• Return false

• Throw exception

33. How to represent
both?

34. Maybe

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

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

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

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

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

40. But is this better than
null?

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

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

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

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

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

46. chain | ﬂatMap
public function chain(callable \$f) : Monad
{
return \$f(\$this->value);
}

47. chain | ﬂatMap
public function chain(callable \$f) : Monad
{
return \$f(\$this->value);
}

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

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

50. 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

51. Will this improve
CRUD?

52. Eloquent::ﬁndMaybe(\$id)

53. Will this improve complex
data transformations?

54. Composable &
Reusable functions

55. Resources
• Professor Frisby’s Mostly Adequate Guide to Functional Programming -
Brian Lonsdorf (@drboolean)