JavaScript is partly a functional language.
To learn JavaScript, we got to learn the functional parts of JavaScript.
In this article, we’ll look at how to pipe functions and functors with JavaScript.
MayBe Functors
A MayBe functor is one that lets us implement a map
function in a different way.
We start off by creating a constructor that stores a value:
const MayBe = function(val) {
this.value = val;
}
MayBe.of = function(val) {
return new MayBe(val);
}
Then we add the methods unique to the MayBe
functor.
We have the isNothing
method to check if this.value
has anything.
The map
method will return something different depending on whether this.value
has something or not.
We add:
MayBe.prototype.isNothing = function() {
return (this.value === null || this.value === undefined);
};
MayBe.prototype.map = function(fn) {
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this.value));
};
Together, we have:
const MayBe = function(val) {
this.value = val;
}
MayBe.of = function(val) {
return new MayBe(val);
}
MayBe.prototype.isNothing = function() {
return (this.value === null || this.value === undefined);
};
MayBe.prototype.map = function(fn) {
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this.value));
};
Then we can use it by writing:
const str = MayBe.of("foo").map((x) => x.toUpperCase())
Then we get the the value
property of the MayBe
instance is 'FOO'
.
If this.value
is null
or undefined
, then map
will return a MayBe
functor with value
being null
.
So if we have something like:
const str = MayBe.of("james")
.map(() => undefined)
.map((x) => `Mr. ${x}`)
We’ll get the final value of value
being null
instead of throwing an error.
Either Functor
The Either
functor allows us to solve problems with branches.
We create a Nothing
or Some
functor and out them in an object.
So we write:
const Nothing = function(val) {
this.value = val;
};
Nothing.of = function(val) {
return new Nothing(val);
};
Nothing.prototype.map = function(f) {
return this;
};
const Some = function(val) {
this.value = val;
};
Some.of = function(val) {
return new Some(val);
};
Some.prototype.map = function(fn) {
return Some.of(fn(this.value));
}
Now if want to hold some data, then we can use the Some
functor.
Otherwise, we use the Nothing
functor to hold some non-existent value.
Monads
A monad is a functor with a chain
method.
The chain
method calls a join
method to call it return an a MayBe
instance if this.value
has a value.
For example, we can write:
const MayBe = function(val) {
this.value = val;
}
MayBe.of = function(val) {
return new MayBe(val);
}
MayBe.prototype.isNothing = function() {
return (this.value === null || this.value === undefined);
};
MayBe.prototype.map = function(fn) {
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this.value));
};
MayBe.prototype.join = function() {
return this.isNothing() ? MayBe.of(null) : this.value;
}
MayBe.prototype.chain = function(f) {
return this.map(f).join()
}
The join
method checks if this.value
is null
or undefined
.
If it is, then we return a null
MayBe
functor.
Otherwise, we return this.value
.
chain
just calls map
and join
together.
This way, if we map something to null
, then it stays null
.
Then we can use this by writing:
let mayBe = MayBe.of({
data: [{
title: 'foo',
children: [{
bar: 2
}]
}]
})
let ans = mayBe.map((arr) => arr.data)
.chain((obj) => map(obj, (x) => {
return {
title: x.title
}
}))
then we get the title
from the object we passed into of
.
Conclusion
A monad is a functor that has the chain
method, which does the mapping and joining together.