Functor Properties

Functor Properties
Functors should Commute
There is one important thing about the mapping function. If you look the following diagram you will see that there are two ways to reach from an integer value of 2 to an Id(4) having a squaring function x=>x*x
The map
should get the same result for Id(4) no matter which of the two possible routes to get there we take. This means map should preserve the structure
- We could first apply the squaring function on the 2 and then get the Id() of the result. This is the blue path of the diagram.
1
var id4 = Id(f(2));
- Or, we could first get the Id(2) and then use
map
to get the Id(4). This is the red path on the diagram.
1
var id4 = Id(2).map(f);
When those two paths always return the same result, then we say the diagram
commutes
The Functor Laws
Additionally, any functor .map()
method must obey two basic laws:
- Firstly the functor
map
should preserves the Identity. This simply means that when we map the id functionx=>x
nothing happens to the Functor.
1
2
3
4
5
import { Id } from "./Id.js"
// Law 1-identity preserving fmap id = id
var id = Id(4).map(x => x); //mapping with the x=>x leave everything as it is
console.log(id);
- Secondly, the
map
should preserve the composition of functions F(f○g) = F(f) ○ F(g)
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Id } from "./Id.js"
// Law 2 composition of functions is preserved
// Id.map (f . g) == Id.map(f) . Id.map (g)
var value = 2
var f =x=>x*x;
var g =x=>3*x;
console.log(Id(value).map(x=>g(f(x)))) //map (f . g) - applying the map on the composition
console.log(Id(value).map(f).map(g)) ; //map f . map g - applying the functions sequentialy using map
Run this
his second law is the reason why we can replace function composition with map chaining because those two properties are equivalent. In the following example you can see that using the fluent map
the Id functor provides
1
Id(client).map(getName).map(toUpperCase).getValue()
is the same as using the function composition on the client
value:
1
toUpperCase(getName(client))
Run the following script to see that effect in action:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Id } from "./Id.js"
import { Client } from "./Client.js"
var getName = client => client.name;
var toUpperCase = name => name.toUpperCase();
var composition = client => toUpperCase(getName(client));
var client = new Client(1, "jake");
//all the following are equivalent :
console.log(composition(client))
console.log(Id(composition(client)).getValue());
console.log(Id(composition(client)).map(x => x).getValue());
console.log(Id(client).map(composition).getValue()); //F(f○g)
console.log(Id(client).map(getName).map(toUpperCase).getValue()); //F(f) ○ F(g)
console.log(Id(client).map(composition).map(x => x).getValue()); //F(f○g) ○ 1
Run this
We are not going to expand on this any further, but feel free to think about how the code relate to the diagram above.