The document discusses implementing a Maybe functor in JavaScript to handle null values safely. A Maybe is a functor that wraps a value and supports map operations. If the wrapped value is null, map returns another Maybe with null instead of propagating the null value. This allows chaining operations while avoiding null checks. The document provides an implementation of a Maybe class with a map method that returns a new Maybe. Examples show how Maybe can be used to safely access nested object properties and avoid null errors.
3. Origin of Null Pointer Exception
● Sir Tony Hoare was designing type systems for the
language ALGOL W and he put “Null Pointer Exception” as
part of the language, because “because it was so easy to
implement”
● In 2009, he called it “The Billion Dollar Mistake”
3
4. What we want:
getCountry('vn').getCity('HCMC').population;
What we have to do
var country = getCountry('vn');
if (country !== null) {
var city = country.getCity('HCMC');
if (city !== null) {
var population = city.population;
}
}
How do we fight NULL?
4
Drawbacks:
● Looks complicated
● Error-prone, especially
when there are many
nesting ifs
● Tedious to write
● Declares a lot of useless
variables
5. ● A Functor is a container of a value
Introduction to Functor
5
A value A container of value
● A simple Functor can be implemented through an ES6 class
class Functor {
constructor(value) {
this.value = value;
}
}
6. One important thing about Functor
6
By definition, a functor needs to implement a
map function
7. Remember Array Map?
7
Let’s look at Array’s map function.
var addOne = (x) => x + 1;
var square = (x) => x * x;
var result = [1, 2, 3]
.map(addOne) // [2, 3, 4]
.map(square); // [4, 9, 16]
8. Implement map for Functor
8
class Functor {
constructor(value) {
this.value = value;
}
map(fn) {
var newValue = fn(this.value);
return new Functor(newValue);
}
}
9. Let’s see it in action (1)
9
var addOne = (x) => x + 1;
var functorOf6 = new Functor(5).map(addOne); // Functor(6)
10. It can also work with any value, for example, an object:
var toUpperCase = (str) => str.toUpperCase();
var exciting = (someName) => someName + ’!!!!!!!’;
var value = { name: 'grokking' };
var containerOfValue = new Functor(value)
.map(thing => thing.name) // Functor(‘grokking’)
.map(toUpperCase) // Functor(‘GROKKING’)
.map(exciting); // Functor(‘GROKKING!!!!!!!’)
Let’s see it in action (2)
10
11. Notice that our actual value is still inside the Functor.
var functorOf36 = new Functor(5)
.map(addOne) // Functor(6)
.map(square); // Functor(36)
var result = functorOf36 + 100; // Functor(36) + 100 ???????
How to get the value inside Functor
11
Let’s add another method to Functor to extract the value.
class Functor {
get() { return this.value; }
}
12. var valueInside = new Functor(5)
.map(addOne) // Functor(6)
.map(square); // Functor(36)
.get(); // 36
var toUpperCase = (str) => str.toUpperCase();
var exciting = (someName) => someName + '!!!!!!';
var valueInside = new Functor({ name: 'grokking' })
.map(people => people.name) // Functor(‘grokking’)
.map(toUpperCase) // Functor(‘GROKKING’)
.map(exciting); // Functor(‘GROKKING!!!!!!!’)
.get(); // 'GROKKING!!!!!!!'
Now we can get the value inside Functor
12
13. class Functor {
constructor(value) {
this.value = value;
}
map(fn) {
var newValue = fn(this.value);
return new Maybe(newValue);
}
get() {
return this.value;
}
}
Full implementation of Functor
13
14. var addOne = (x) => null; // we return Null
var square = (x) => x * x;
var valueInside = new Functor(1)
.map(addOne) // Functor(null)
.map(square); // Functor(null * null) ????
.get(); // ????
But we are still affected by NULL
14
The solution is to turn our functor into a Maybe
15. ● Maybe is a kind of Functor that is Null-
friendly. Real magic happens in the map
function
● Maybe that contains a real value (not null)
is usually called a Just / Some
● Maybe that contains null value is usually
called a Nothing / None
Introduction to Maybe
15
Maybe(value) = Just(value)
Maybe(null) = Nothing
16. class Maybe {
constructor(value) { this.value = value; }
map(fn) {
if (this.value !== null) { // Magic happens here
var newValue = fn(this.value);
return new Maybe(newValue);
}
return new Maybe(null);
}
getOrElse(fallBackValue) {
return (this.value === null) ? this.value : fallBackValue;
}
}
Let’s implement Maybe
16
17. It’s pretty much the same as Functor’s example. The only difference is that we use
getOrElse instead of get
var addOne = (x) => x + 1;
var square = (x) => x * x;
var result = new Maybe(3)
.map(addOne) // Maybe(4)
.map(square) // Maybe(16)
.getOrElse(0); // 16
Maybe in action
17
18. When Maybe contains NULL, it will not map anymore
var result = new Maybe(3)
.map(x => null) // Maybe(null)
.map(x => x * x) // Maybe(null)
.getOrElse(0); // 0
var result = new Maybe(3)
.map(x => x + 1) // Maybe(4)
.map(x => null) // Maybe(null)
.getOrElse(0); // 0
When Null happens
18
19. Some real world examples
var family = {
grandma: {
mother: {
you: 'YOUR NAME HERE'
}
}
};
var you = new Maybe(family.grandma)
.map(grandma => grandma.mother)
.map(mother => mother.you)
.getOrElse('DEFAULT NAME');
// returns “YOUR NAME HERE”
var family = {
grandma: null
};
var you = new Maybe(family.grandma)
.map(grandma => grandma.mother)
.map(mother => mother.you)
.getOrElse('DEFAULT NAME');
// returns “DEFAULT NAME”
19
We can use it to safely access nested objects
20. Compare with what we used to do
20
var you = family.grandma.mother.you || 'DEFAULT NAME';
var you = 'DEFAULT NAME';
if (family.grandma &&
family.grandma.mother &&
family.grandma.mother.you)
{
you = family.grandma.mother.you;
}
var you = new Maybe(family.grandma)
.map(grandma => grandma.mother)
.map(mother => mother.you)
.getOrElse('DEFAULT NAME');
Short, but unsafe
Safe, but long and
nested
Safe and flattened
21. var population = new Maybe(getCountry('vn'))
.map(country => country.getCity('HCMC'))
.map(city => city.population)
.getOrElse(0);
Some real world examples
var country = getCountry('vn');
if (country !== null) {
var city = country.getCity('HCMC');
if (city !== null) {
var population = city.population;
}
}
21
We can rewrite our example earlier
Into shorter and flattened code
22. ● Is just a container of value
● Is a fundamental concept in
Functional Programming. From
there you can explore Applicative
and Monad
● Is a kind of functor that checks null on each map
● Helps flatten your code
● Is used widely in many other languages:
- Haskell, Elm: Maybe
Recap
22
Maybe
Functor
23. References
● https://github.com/fantasyland/fantasy-land Fantasy Land Specification
● https://cwmyers.github.io/monet.js/ Powerful abstractions for Javascript
● http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
Functor, Applicatives, Monads in pictures
● https://en.wikipedia.org/wiki/Functor (looks scary with all the math)
● http://www.mokacoding.com/blog/functor-applicative-monads-in-pictures/ (Maybe in
Swift)
● http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html Java 8
Optional
23