Asynchronous and Single-threaded JavaScript?
JavaScript is a single-threaded language and, at the same time, also non-blocking, asynchronous and concurrent. This article will explain to you how it happens.
Check the third part of our Power of functional programming in JavaScript article series. This time our JavaScript expert explains more about Functor and Monad Maybe.
Often keeping unmodifiable is difficult to maintain. The pattern of wrapping data into a container comes to the rescue. It secures the values so that handling them is safe with the exclusion of side effects.
If you are new here make sure to check my last 2 parts regarding functional programming on The Codest blog about:
It has no complex logic. Its main task is to wrap the context and perform the functions it receives from the outside on them. Each time the value changes, a new container instance is repackaged and returned. When calling the map method, which takes a specific action, it returns a new container instance with the value returned by the passed function, while maintaining the non-modifiable principle.
const Functor = value => ({
map: fn => Functor(fn(value)),
chain: fn => fn(value),
of: () => value
});
map – useful when you want to change the state of a value in a container but don’t want to return it yet.
chain – used if you want to pass a value to a function without modifying the container state. Usually at the end of map calls.
of – return current value
const randomInt = (max) => Math.floor(Math.random() * (max + 1))
const randomNumber = randomInt(200) // returns number between 0 and 200
decrease(randomNumber) // returns (number between 0 and 200) - 1
const randomIntWrapper = (max) =>
Functor(max)
.map(increase) // max + 1
.map(multiplyBy(Math.random())) // Math.random() * (max + 1)
.map(Math.floor) // Math.floor(Math.random() * (max + 1))
const randomNumber = randomIntWrapper(200)
randomNumber.of() // returns number between 0 and 200
randomNumber.chain(decrease) // returns (number between 0 and 200) - 1
Sometimes, in addition to the functions that trigger the new state of the value, you need additional logic hidden in the container. This is where monad comes in handy, as it is an extension of functor. It can, for example, decide what should happen when the value has a certain value or what path the next actions are to take.
Monad maybe solves the problem of values that return not true. When this happens, subsequent map calls are ignored, but it allows you to return an alternative by calling the getOr method. It allows you to avoid the use of if / else operators, which are popular in imperative programming. This monad consists of three containers:
Nothing – runs when a value that is not true falls into the container or filter method returns false. It is used to simulate the execution of a function. This means that this container receives the function but does not execute it.
Just – this is the main container that performs all the functions, but if the value changes to a false value or filter method returns false, it will pass it to the Nothing container.
Maybe – I take the initial value and decide what container to call at the start.
const Just = value => ({
map: fn => Maybe(fn(value)),
chain: fn => fn(value),
of: () => value,
getOr: () => value,
filter: fn => fn(value) ? Just(value) : Nothing(),
type: 'just'
});
const Nothing = () => ({
map: fn => Nothing(),
chain: fn => fn(),
of: () => Nothing(),
getOr: substitute => substitute,
filter: () => Nothing(),
type: 'nothing'
});
const Maybe = value =>
value === null || value === undefined || value.type === 'nothing'
? Nothing()
: Just(value)
Now let’s build the previous example about the condition. If max is greater than zero, the function will be executed. Otherwise, it will return 0.
const randomInt = (max) => {
if(max > 0) {
return Math.floor(Math.random() * (max + 1))
} else {
return 0
}
}
const bookMiddlePage = 200
const randomPage = randomInt(10) || bookMiddlePage // returns random
const randomPage = randomInt(-10) || bookMiddlePage // returns 200
const randomIntWrapper = (max) =>
Maybe(max)
.filter(max => max > 0) // value false ignores further calls
.map(increase)
.map(multiplyBy(Math.random()))
.map(Math.floor)
const bookMiddlePage = 200
// Just container
const randomPage = randomIntWrapper(10).getOr(bookMiddlePage) // returns random
// Nothing container
const randomPage = randomIntWrapper(-10).getOr(bookMiddlePage) // returns 200