Global state & functional programming

Do “global state” and “functional programming” contradict each other? I often find myself running into a situation where I don’t know how to decide whether a function should get the values it’s working on from a global variable or if I should rather pass those values as parameters to the function.

As an example - the 25 + 5 challenge (in pure JS):

I have one global object holding state values, and six functions.
The first three functions are triggered through click events (toggleTimer, reset, updateLength)
The other three are conditionally called by the first three, depending on current values.

The reset function for example:

function reset() {
    clearInterval(currVals.intervalID);
    currVals = {
      ...initialVals
    };
    updateControlsDisplay();
    updateTimerDisplay();
  }

It calls updateControlsDisplay() and updateTimerDisplay() without parameters. I could rewrite the function like this:

function reset() {
    clearInterval(currVals.intervalID);
    currVals = {
      ...initialVals
    };
    updateControlsDisplay(currVals.session, currVals.break);
    updateTimerDisplay(currVals.timeLeft, currVals.phase);
  }

Now updateControlsDisplay and updateTimerDisplay don’t depend on global state anymore, just on what the caller gives them. Both work obviously, but am I guessing right that the second alternative would be “better/cleaner”? Are there situations where the first alternative would be preferrable?

Neither method is better or more correct and both have their uses and abuses.

I find that the closer to the UI the code is, the more likely I am to have functions without parameters. For instance, if I’m writing a function that will get called by a click event in pure JS, they usually have no parameters and a bunch of document.getElementById().value statements in them to gather information which gets passed to pure functions. Similarly, if I’m creating a class and defining methods, there is a much greater chance the method will use data stored on an object property rather than getting that passed as an argument. But if I’m writing functions for a library or backend, I opt for pure functions unless there is a very good reason not to (and there usually isn’t for me). Even if I can’t use a pure function for some reason, you can wrap a chunk of code with a pure function and keep the “global” stuff confined to the scope of the wrapper function.

I’ve seen old code that lead to the textbook warnings about global variables and functions without parameters. There are piles of old fortran with a list of global variables that can get changed anywhere in the program that are super fun to debug. Some people even helped and removed the functions and replaced them with goto statements. The difference between this and UI functions/class methods is where the data lives. Classes encapsulate the methods and data, so even without function arguments, it’s fairly easy to find and track changes to the data. With undisciplined use of globals though, it’s hard.

In your examples, I would write it the second way, but just because I could write updateTimerDisplay() to work with whatever time data I gave it and reuse the code because you may need it elsewhere where the data it needs is not in scope. reset() is much closer to the UI and when I use it, I just want everything reset and I know it’s going to touch a bunch of variables as it works. Conversely, if you’re making a UI class, I would definitely write reset() and updateTimerDisplay() as methods without arguments and let them use the data stored on the object.

Please don’t conflate “variables with global scope” with “global state”.

The first one, often referred to as “global variables”, are a coding sin.

But “global state” can be 100% compatible with functional programming. Like in redux, you can have a global store/state. The difference is that it is not being inherited from a surrounding scope, but is being passed in as a parameter. It is “global” in the sense that it can be accessed from anywhere (with a little work) but because it is passed in as a parameter, the function can remain pure.

Yes that’s what I’ve heard (and agree with), but how could I refactor the above code to avoid global variables altogether?

For instance, several functions need to know the value of isTimerRunning. If I don’t have that in a global variable, the only thing I can think of is reading and storing it in the DOM (as a value attribute on the button). Same for timeLeft etc.

My logic wouldn’t entirely live inside my script, with the DOM only receiving values, the script would depend on getting values from the DOM, and that seems even more horrible than having global variables.

To avoid global variables altogether, you need to pass in the variables you are. That is one of the ideas of a pure function - it only depends on the data passed into it.

Yes I’m aware of that, I just don’t know how to translate that to my code.

My toggleTimer function sets up an interval. The reset function needs to clear that interval. How is reset supposed to know the intervalID, when that intervalID is only known inside the scope of toggleTimer?

I have a feeling that the answer includes the word “closure”. But I’m too thick to get it done. I’ve been hitting this wall for a while now and it’s really frustrating.

Again, I would just pass it in. I would have:

btnStartStop.addEventListener("click", () => toggleTimer(currVals));

and then:

function toggleTimer(currVals) {
  // ....

Of course, you can use a different variable name in the function to avoid shadowing the variable.

Where are currVals coming from in your example, if not from an object outside the scope of toggleTimer (=> global scope)? And how would reset get access to those values?

Right, it’s the global object. But the point is not that you can’t have variables in an outer scope, but that you shouldn’t reference those out of scope variables. By passing them in, you are keeping the function pure - what it does is based solely on its inputs. That passed in variable can be anything - the function doesn’t know or care that it comes from the outer scope.

I give up. I have three functions that are called through DOM events. What they do depends on one object outside of their scope, and they all modify that object in some way. I find no way to refactor my code to only have pure functions.

So I just wrapped the whole block:

function pomodoro(){
    // everything happens inside here
};
pomodoro();

No global variables anymore. Problem solved. :neutral_face:

Your code is already scoped inside the DOMContentLoaded handler function (you do not need the pomodoro function).

I think you might be trying to take the idea of pure and non-mutation too far.

I guess for the interval id it might be possible to combine the functions that use it into one function that has a closure on the id so it is contained inside the function but still accessible (I didn’t really think about how this would be done though).

But everything on a (huge) website (with one script) is inside the DOMContentLoaded handler, so I thought that would at least wrap the pomodoro stuff to where it belongs. But yeah you’re right, doesn’t make sense if there’s only the pomodoro code inside the script.

I really want to understand this. Everyone says that global variables are a no-go, but literally EVERY code example I come across uses them. I’ve NEVER seen a tutorial that would address this issue and show me how to refactor code to avoid this, and how to handle a situation when two or more functions need access to one object.

First off, global and top-level variables are not the same thing. As long as your code is inside a scope you control you have avoided half the issues surrounding global variables. Global variables are accessible from any code, yours, and mine. If we both write library code that is used on the same page and we both set some variables or methods on the global object, we run the risk of stepping on each other’s code. Which is in part why the Module Pattern was “invented” (encapsulation).

Keeping state is unavoidable, sure you can compose functions and pass values around but you will more than likely need to store and retrieve some program state at some point. It is how you interact with and update the “state” of the program that is important (predictability), a state-less program isn’t very useful. The MV* Patterns and view libraries/frameworks (like React, or Vue) are a reaction to some of the issues (from for example spaghetti jQuery code). You can also look at state management ideas from something like Redux and immutability from something like Immer.

BTW, any program that does DOM manipulation is not pure, DOM manipulation is a side-effect. A program with no side-effects is barely a program, especially in web development.

As said, you don’t have global values unless you’ve explicitly attached them to the global object (window in the browser).

But

Functional programming is literally programming with functions. And some things fall out of that, for example that functions should be pure and do a single thing, data should be immutable, functions should be composed together. In terms of irl programming, using functional styles tends to make the code easier think about and to test.

In practice, the issue with everything being pure is that it precludes state and side effects and without state and side effects you can’t do anything useful. You can’t use time because time is non-deterministic. You can’t use timers because they depend on time. You can’t print things to the screen. Etc etc. There are [purely] functional ways around this, but JS does not have the constraints of a more conservative functional language. You can find lots of pure examples, but they’ll be in Haskell or something similar. And so you put the state into black boxes away from the functional parts of the code, then call into it.

However, wrapping a bunch of logic in a function doesn’t functional programming make (in the same way that putting everything inside an object does not make the code object-oriented).

If you want to make your code cleaner (and more “functional”) then you need to seperate out the functionality – at the minute you’ve just dumped everything in the same place. There is no functional core to it, you are reading from the DOM, runnning the timer and writing to the DOM all in the same place.

To be clear

  • Any function that uses setInterval or writes to the DOM cannot be pure by definition.
  • You need to store values somewhere to allow you to stop and restart the timer, so you have to hold onto that state somewhere, and it’s sensible if it’s in one place.
  • The timer itself cannot be pure, but you can seperate it out
  • The work to write those values (either print them to the console or write to the DOM, doesn’t matter) can be seperated out, into a renderer function/s
  • The work to read those values doesn’t need to exist: once the timer exists you can call functions on it directly from controls in the DOM.

Thx @lasjorg & @dancouper, I think I indeed tried to overdo it a little. I mean for this example, the only pure function I could imagine is something like

function getTime(currentTime){
    return currentTime - 1
}

The rest is messy DOM manipulation depending on a timer and user inputs. So I hereby declare my code as sufficiently clean and readable and move on (still want to make it work in React though).

EDIT Except one thing - what exactly did you mean by this:

Erm sorry, reading back I managed to delete a quite a bit of the sentence and I can’t quite figure out exactly what I wrote. That bit was about storing state and config in the DOM, which is what you’re currently doing with stuff like highly specific IDs and things that you then carefully split. If you have a container for the state (which I meant to write, not timer), then you just call functions on that

Pardon my thickness.
When I hear “DOM”, I think of what I see when I open my browser’s dev tools, so basically the HTML. Where am I currently storing state and config in the DOM?
And does “a container for the state” mean that I have an object in my top level scope to wrap all my variables?
And does “call functions on that [container]” mean that my functions just grab their values from that object?