Stateful Monads in JavaScript — Part 1

July 24, 2018 0 Comments

Stateful Monads in JavaScript — Part 1

 

 

Dealing with stateful computations can be a real pain when you are writing purely functional JavaScript code.

It can result in things like undesired variable declarations, and if you are not using things like Redux, a lot of boilerplate state management code in every function which depends on the state.

In this 2 part series, I will be explaining everything you need to know about State — a term that all JavaScript developers come across in their adventures- focusing a stateful monads. Let’s dive in.

A Monad is a pattern used to describe computations as a series of steps. They are extensively used in functional programming languages to prevent side effects, and can also be used in other languages to manage complexity.

A monad consists of the following components:

  • Type constructor — A feature that creates a monadic type for the underlying type.
  • Unit — A function that wraps a value of an underlying type into a monad.
  • Bind — This function is used to chain the operations on a monadic value.

JavaScript comes with a datatype called State. It is generally defined as a product type with a fixed type state and a resultant variable type.

This will be a 2 part series on Stateful Monads in JavaScript. Part 2 will be out soon.

Lets start by creating an empty project directory.

$ mkdir <name>
$ cd <cd name>
$ touch index.js

Open this directory in a code editor. I like to use VS Code.

Let’s also install the useful crocks library as a dependency to this project.

$ yarn add crocks

Next, create a new file called logger.js and paste this example code inside it. Similarly, create a new file called helpers.js and paste this example code inside it.

Import the logger.js file as log inside the index.js file. Also import the State constructor from the Crocks library. We’re now ready to get going!

const log = require('./logger')
const State = require('crocks/State')

With our setup ready, we can now create an instance of State called rajat. Here, rajat will have take in a number type and return a string as resultant.

To construct a State we need a special function to pass to the constructor. This function needs to accept a value of the fixed state and return the resultant with the state.

The crocks library provides a function called Pair that will do just that. So, lets import it into our code.

const Pair = require('crocks/Pair');

Using this function, we can now create an instance of State as shown below:

const rajat = State(state => Pair('value', state));
log(rajat);

Give this a save, and run the node command in the terminal. You will then get the output in the terminal as State Function.

However, nothing has really happened yet as our State will not do anything unless we explicitly tell it to do something. And for that, we need to give our State some data to work with.

Rewrite the log statement with the .runwith method as shown below.

log(rajat.runwith(1000);

Here, I am setting the number 1000 as the initial state of the function. Run the node command again and now you will get the output as Pair('value', 1000).

If you want to pluck out the resultant from the Pair, you need to use the .fst method. Similarly, you will need to use the .snd method to grab the state from the Pair. As an example, restructure rajat to perform some mathematical operation, and grab its state using the .snd method like this:

const rajat = State(state => Pair(state+1000, state));
log(rajat.runwith(1000).snd());

This will give us the output as 2000, and that is what will be stored in the state. See what you will get by replacing the .snd method with .fst.

Take a look at the code from the previous section. So far, every time we need to modify the resultant, we need to use the constructors.

Instead, we can use the .map method to inject a function and to let the type itself take care of all the internal things for us.

To use the map method, let’s create a new constructor that will get us the current state.

const getState = () => State(state => Pair(state, state));

As you can see we have simply created an instance of State and return the state as both the resultant and the state.

Let’s use this in the log statement like this:

log(getState().runWith(1000).snd());

Running the node command in the terminal, we will get the output as 1000. Replacing the snd method with fst will also give us the output as 1000.

In the helpers.js file, create a new function called add that will take two numbers as input and return their sum as output.

const add = x => y => x + y;

We can then use this function inside the index.js file. First, import the add function into the index.js file.

const {add} = require('./helpers.js');

Then, use it inside the log statement like this:

log(getState().map(add(1000).runwith(1000).fst());

Run the node command and you will get 2000 as the ouput.

The amazing thing here is that the resultant is not constrained to a type like the state is. In fact, we can change the type of the resultant as we want. To understand this, create a new function in the helpers.js file called plural.

const plural = (single, multiple) => num => 
${num} ${Math.abs(num) === 1 ? single : multiple}

This function takes in a tuple of two strings as the context, takes another number as its data, and returns to a string. Import this function into the index.js file.

const {add, plural} = require('./helpers.js');

Use the plural function inside another function called amazingRajat.

const amazingRajat = plural('Rajat', 'Rajats');

Lets use this function inside the log statement as shown below.

log(getState()
.map(amazingRajat)
.runWith(1000)
.fst()
)

The ouput of this will be something like 1000 Rajats. If you replace the 1000 with 1, you will get the output as 1 Rajat.

Note: You can get rid the of getState by importing its replacement from the crocks library.

const {get} = require('crocks/State');

We can use the get construction helper to lift the function that maps the state and update the resultant with the result.

To start with, create a new file in the project directory called data.js and write the following code inside it.

(function(root) {
const burgers =
{ burgers: 4 }
const tacos =
{ tacos: 10 }
root.data = {
burgers,
tacos
}
})(window)

Then bring in the burgers object into the index.js file.

const {burgers} = require('./data');

That takes care of the intial code setup. Now create a new instance of state called getBurgers as shown below. Also call it inside the log.

const getBurgers = get()
log(getBurgers.runWith(burgers))

Running the node command will us the output as Pair({},{}). This is not what I was expecting.

To solve this issue, I need to grab the value of burgers from the State and insert it into the Pair’s resultant. This can be done with the help of prop function.

const prop = require('crocks/Maybe/prop')

The prop function gives us value of a property on an object wrapped in a Just. If the property does not exist, then we will get the output as Nothing. Rewrite the getBurgers using prop function as shown below:

const getBurgers =
get()
.map(prop('burgers'))

This will give us the output as Just 4. But usually, we will want to the output without any wrapping. For that, we need to use the evalWith method as it will unwrap the resultant. Use evalWith method inside the log statement and see what output you will get.

log(
getBurgers
.evalWith(burgers)
)

If we pass a function to get, it will map over the State and substitute it with the result, removing the additional map.

const getBurgers =
get(prop('burgers'))

Another amazing function inside the crocks library is the one called option. option will either unwrap the Just statement and return the value or return another output in case of Nothing. First, lets import this function into the index.js file.

const option = require('crocks/pointfree/option');

Then use the option function inside getBurgers as shown below.

const getBurgers = 
get(prop('burgers'))
.map(option(0))

With this code, we will get the output as just 4. If for some reason our output is of the Nothing variety, we will get 0 as the ouput.

Stateful computations require the ability for their state to change overtime. It is would be quite helpful to us if we could easily update our state over time.

Inside the index.js file, create a new function called putState. This function will take a given state and return a new instance that ignores the current state. This new instance will have a Pair with Unit as the resultant and the given state as the state. We will need to import Unit from the crocks library.

const Unit = require('crocks/Unit');

Next, let’s write the putState function using State, Pair, and Unit as shown below:

const putState = state => 
State(() => Pair(Unit(), State))
log (
putState("Taj Mahal")
.runWith("Agra")
)

This will give us the output as Pair((), "Taj Mahal"). Replace the runWith method with evalWith and you will get the unit () as the resultant.

State has provided us with another method called execWith. Using it instead of evalWith will unwrap the state from the Pair and throw out the resultant, which in this case is a unit ().

This method is extremely helpful when you want to reset your state to an initial value. To learn it’s done, create a new function called reset.

const reset = () => 
putState('Taj Mahal')

This function takes Nothing and returns the result of calling putStatewith our pointed value of 'Taj Mahal'. Replace the call to putState inside the log statement with a call to reset. You will see in the output that we are still getting Taj Mahal as our state, even though we started with the Agra.

Note: You can get rid the of putState by importing its replacement from the crocks library.

const {put} = require('crocks/State');

In the previous section, we learned how we can update the state. However, updating the state can sometimes make it difficult for us to make any modifications to the state that are based on its previous values.

Lets take a look at how we can use functions to modify the state’s value.

These functions are of a special kind and must have the same kind of type in their input and output.

Lets start by creating a new construction helper that takes a function defining how our state should be modified.

const modifyState = fn => 
State(s => Pair(Unit(), fn(s)))

Here I have created a function called modifyState that accepts a function and returns a State instance that takes in the current state and returns a Pair of Unit as resultant and the result calling the function with the current state as the new state.

Our next step would be to call the modifyState function inside the log. But before that, we need to write another function to define the modification.

Write a const called state which has an object describing a certain number of pizzas.

const state =
{pizzas: 0}

I want my state modifying function to increase the value of pizzas by 1. For that, we need a function that takes and returns an object.

The crocks library has provided us with a binary function called mapProps that provides us a means to modify the state.

const mapProps = require('crocks/helpers/mapProps');

To use mapProps, we need to apply an object of functions that will replace the given key’s value with the result of applying the original value to the provided function. Here, we will target the key of pizzas and use the add function on it to increment its value by 1.

log(modifyState(mapProps({pizzas: add(1)}))
.execWith(state)
)

Make sure that you have imported the add function properly.

const {add} = require('./helpers')

Run the node command and you will get the output as {pizzas: 1}. That’s it!

This concludes Part 1 of this series on Stateful Monads in JavaScript. Part 2 will be out soon. Hope you enjoyed what we’ve learned, and stay tuned :)


Tag cloud