What is the purpose of the state monad?
Asked Answered
M

4

15

I am a JavaScript developer on a journey to up my skills in functional programming. I recently ran into a wall when it comes to managing state. When searching for a solution I stumbeled over the state monad in various articles and videos but I have a really hard time understanding it. I am wondering if it is because I expect it to be something it is not.

The problem I am trying to solve

In a web client I am fetching resources from the back end. To avoid unnecessary traffic I am creating a simple cache on the client side which contains the already fetched data. The cache is my state. I want several of my modules to be able to hold a reference to the cache and query it for its current state, a state that may have been modified by another module.

This is of course not a problem in javascript since it is possible to mutate state but I would like to learn more about functional programming and I was hoping that the state monad would help me.

What I would expect

I had assume that I could do something like this:

var state = State.of(1);
map(add(1), state);
state.evalState() // => 2 

This obviously doesn't work. The state is always 1.

My question

Are my assumptions about the state monad wrong, or am I simply using it incorrectly?

I realize that I can do this:

var state = State.of(1);
var newState = map(add(1), state);

... and newState will be a state of 2. But here I don't really see the use of the state monad since I will have to create a new instance in order for the value to change. This to me seems to be what is always done in functional programming where values are immutable.

Masto answered 29/1, 2015 at 20:46 Comment(1)
If you use the state monad you can change map or State to change the meaning of assignment, update, get functions. Its sort of like treating a variable like a file object or getter/setter so you can later change how assignment is done, will you update a bunch of other states, will you log the state, will you get and save the state in a file or in a database, will you keep a history of your assignment statements, do you want to add undo/redo to your states?Mccloud
A
15

The purpose of the state monad is to hide the passing of state between functions.

Let's take an example:

The methods A and B need to use some state and mutate it, and B needs to use the state that A mutated. In a functional language with immutable data, this is impossible.

What is done instead is this: an initial state is passed to A, along with the arguments it needs, and A returns a result and a "modified" state -- really a new value, since the original wasn't changed. This "new" state (and possibly the result too) is passed into B with its required arguments, and B returns its result and a state that it (may have) modified.

Passing this state around explicitly is a PITA, so the State monad hides this under its monadic covers, allowing methods which need to access the state to get at it through get and set monadic methods.

To use the stateful computations A and B, we combine them together into a conglomerate stateful computation and give that conglomerate a beginning state (and arguments) to run with, and it returns a final "modified" state and result (after running things through A, B, and whatever else it was composed of).

From what you're describing it seems to me like you're looking for something more along the lines of the actor model of concurrency, where state is managed in an actor and the rest of the code interfaces with it through that, retrieving (a non-mutable version of) it or telling it to be modified via messages. In immutable languages (like Erlang), actors block waiting for a message, then process one when it comes in, then loop via (tail) recursion; they pass any modified state to the recursive call, and this is how the state gets "modified".

As you say, though, since you're using JavaScript it's not much of an issue.

Alceste answered 29/1, 2015 at 21:29 Comment(3)
Thanks for the description. I think what confuses me is that what you describe with A receiving a state and passing it's result to be just sounds like normal function composition to me. But anyway, I guess state is not the solution for my problem.Masto
It's pretty close to function composition conceptually, but you can think of things as happening at two levels: the monadic function (e.g. A) will have the normal args -> result layer like other functions, but at the same time there's another layer that's state -> state. The state is threaded through each function on its own level of composition; on the regular level, functions can return a result or be more like a procedure where they just do side-effects (mutating the underlying state) and return a void or unit or whatever it is in the particular language.Alceste
@LudwigMagnusson There are a couple of other monads that have this compositional nature: the Either and Maybe monads. These monads can cause the composition chain to, in effect, exit early -- and in the case of the Either monad, to exit with some other sort of result (e.g. a string with an error message). You might find this tutorial series interesting, particularly part two.Alceste
G
7

I'm trying to answer your question from the perspective of a Javascript developer, because I believe that this is the cause of your problem. Maybe you can specify the term Javascript in the headline and in the tags.

Transferring of concepts from Haskell to Javascript is basically a good thing, because Haskell is a very mature, purely functional language. It can, however, lead to confusion, as in the case of the state monad.

The maybe monad for instance can be easily understood, because it deals with a problem that both languages are facing: Computations that might go wrong by not returning a value (null/undefined in Javascript). Maybe saves developers from scattering null checks throughout their code.

In the case of the state monad the situation is a little different. In Haskell, the state monad is required in order to compose functions, which share changeable state, without having to pass this state around. State is one or more variables that are not among the arguments of the functions involved. In Javascript you can just do the following:

var stack = {
  store: [],
  push: function push(element) { this.store.push(element); return this; },
  pop: function pop() { return this.store.pop(); }
}

console.log(stack.push(1).push(2).push(3).pop()); // 3 (return value of stateful computation)
console.log(stack.store); // [1, 2] (mutated, global state)

This is the desired stateful computation and store does not have to be passed around from method to method. At first sight there is no reason to use the state monad in Javascript. But since store is publicly accessible, push and pop mutate global state. Mutating global state is a bad idea. This problem can be solved in several ways, one of which is precisely the state monad.

The following simplified example implements a stack as state monad:

function chain(mv, mf) {
  return function (state) {
    var r = mv(state);
    return mf(r.value)(r.state);
  };
}

function of(x) {
  return function (state) {
    return {value: x, state: state};
  };
}

function push(element) {
  return function (stack) {
    return of(null)(stack.concat([element]));
  };
}

function pop() {
  return function (stack) {
    return of(stack[stack.length - 1])(stack.slice(0, -1));
  };
}

function runStack(seq, stack) { return seq(stack); }
function evalStack(seq, stack) { return seq(stack).value; }
function execStack(seq, stack) { return seq(stack).state; }
function add(x, y) { return x + y; }

// stateful computation is not completely evaluated (lazy evaluation)
// no state variables are passed around
var computation = chain(pop(), function (x) {
  if (x < 4) {
    return chain(push(4), function () {
      return chain(push(5), function () {
        return chain(pop(), function (y) {
          return of(add(x, y));
        });
      });
    });
  } else {
    return chain(pop(), function (y) {
      return of(add(x, y));
    });
  }
});

var stack1 = [1, 2, 3],
  stack2 = [1, 4, 5];

console.log(runStack(computation, stack1)); // Object {value: 8, state: Array[3]}
console.log(runStack(computation, stack2)); // Object {value: 9, state: Array[1]}

// the return values of the stateful computations
console.log(evalStack(computation, stack1)); // 8
console.log(evalStack(computation, stack2)); // 9

// the shared state within the computation has changed
console.log(execStack(computation, stack1)); // [1, 2, 4]
console.log(execStack(computation, stack2)); // [1]

// no globale state has changed
cosole.log(stack1); // [1, 2, 3]
cosole.log(stack2); // [1, 4, 5]

The nested function calls could be avoided. I've omitted this feature for simplicity.

There is no issue in Javascript that can be solved solely with the state monad. And it is much harder to understand something as generalized as the state monad, that solves a seemingly non-existing problem in the used language. Its use is merely a matter of personal preference.

Given answered 20/11, 2015 at 14:1 Comment(2)
well just to make this clear: you don't need to use the State Monad in Haskell to mutate state and indeed you don't really mutate state in it anywhere - basically the idea is to pass around the changing state as an additional parameter (make it explicit) and the State-Monad abstracts away this parameter passing - of course there are ways to really mutate stuff but this is usually done in either the IO or the ST-MonadDomesday
@Carsten: Thanks, that helped. I updated my answer accordinglyGiven
C
4

It indeed works like your second description where a new immutable state is returned. It isn't particularly useful if you call it like this, however. Where it comes in handy is if you have a bunch of functions you want to call, each taking the state returned from the previous step and returning a new state and possibly another value.

Making it a monad basically allows you to specify a list of just the function names to be executed, rather than repeating the newState = f(initialState); newNewState = g(newState); finalState = h(newNewState); over and over. Haskell has a built-in notation called do-notation to do precisely this. How you accomplish it in JavaScript depends on what functional library you're using, but in its simplest form (without any binding of intermediate results) it might look something like finalState = do([f,g,h], initialState).

In other words, the state monad doesn't magically make immutability look like mutability, but it can simplify the tracking of intermediate states in certain circumstances.

Chronister answered 29/1, 2015 at 21:29 Comment(2)
I use the compose function from ramda to accomplish what you describe, but I cant really see how state changes the game. Thanks for the clarification though.Masto
This is my thought as well you could compose a curried function waiting for a state object, since I'm new to fp I'm not sure what is the benefits of this approach vs composeDonaldson
C
1

State is present everywhere. In class, it could be the value of its properties. In programs it could be the value of variables. In languages like javascript and even java which allow mutability, we pass the state as arguments to the mutating function. However, in languages such as Haskell and Scala, which do not like mutation(called as side-effects or impure), the new State (with the updates) is explicitly returned which is then passed to its consumers. In order to hide this explicit state passes and returns, Haskell(and Scala) had this concept of State Monad. I have written an article on the same at https://lakshmirajagopalan.github.io/state-monad-in-scala/

Carilla answered 11/1, 2017 at 8:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.