2

Let's say you have a tic-tac-toe app.

  • There's the game state which can be represented by an array of size 9.
  • There's your "reducer" computes new state from old state and player actions.
  • Then there's your renderer which updates the UI based on the new state.

Great! Functional programming basics. Unfortunately, this is where most online resources end. They all talk about how to write logic in a pure manner, not having side-effects, not mutating data or reassigning values. What they don't expand a lot on is what happens next:

  1. How new state is held by the app?

    If I mark a cell on the board "X", the app takes my move, computes new state, re-renders the grid. But I cannot mutate the original array, nor can I assign it to the same variable. How does the app hold the new state?

  2. How can rendering be pure in a web app?

    If I mark a cell on the board "X", the app takes my move, computes new state, re-renders the grid. But to re-render the grid, I'd be manipulating the DOM, which is effectively mutating a data structure. How does one update the DOM functionally?

I come from a web dev background, where it's usually about waiting for events, mutating data and manipulating DOM - rarely any FP in sight. Also, most talk about holding state in the stack, usually in a form of recursion, which is not a thing in web apps. So I'm having a hard time wrapping my head around the concept, especially when materials describe patterns that are not applicable. Hints to the right direction is appreciated.

3

Functional programming encompasses a range of techniques. Immutability and purity are important concepts because they allow for equational reasoning, but using them is often inconvenient. In fact, most functional programming other than Haskell do not support a completely pure and immutable programming style. Instead, Scheme, OCaml, or JavaScript are functional–imperative languages where mutating state is normal.

If we look at different ways to do functional programming, the focus does not seem to be immutability or equational reasoning, but that we use techniques such as first-class functions, closures, and recursive algorithms. If you're using JS, you have probably already used loads of functional techniques!

Instead of getting rid of state, it is often already quite helpful to make all state explicit. E.g. instead of a method that updates a game state object, have the method return a new game state. It is still perfectly fine to reassign your variables. It would not be unusual to see something like this at the border of your system:

var state = GameState(initial);
userInterface.onInteraction(function updateState(e) {
  state = calculateNewState(state, e);
  userInterface.drawState(state);
});
userInterface.drawState(state);

This could in theory be changed to use recursion instead of a mutable variable, but if the two approaches are functionally equivalent then it makes sense to use the more readable approach, whether it's more “pure” or not. With the same argument, it's still perfectly fine to use foreach-loops in functional code, even though they only make sense with imperative programming. But that is the crux of the matter:

State isn't bad. Shared mutable state makes a program more complex, and complexity is risky. Local mutable state (e.g. a variable being reassigned) is totally fine, even in FP.

In a web app, the DOM is arguably shared mutable state because it is global. This doesn't mean you can't use FP, this means that it makes sense to isolate your logic from this state. In your particular case, it makes sense that the DOM is never the canonical data model of your application. Instead, the DOM could be re-drawn from the actual game state. The DOM would then not contain any meaningful state from the perspective of your app, as it's just an output target.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.