bounding brokenness

Civilising global state

It seems to be universally agreed that global mutable state is considered harmful. It makes dependencies less clear, means that functions are harder to test, introduces races, has to be handled separately when exceptions occur, and so on.

Yet anyone who has programmed for a while knows that there are always situations where global state is useful and convenient. Perhaps adding it to every single function call’s signature is a pain; perhaps you need to set up a callback and need some data there but can’t create a closure for it; perhaps you’d like to look up the stack for security reasons; perhaps you simply don’t want to refactor 90% of your code. (You might think you’re avoiding the pitfalls of global state by using singletons or static variables, but you’d be wrong.)

Funnily enough, we’ve had a solution to these use cases since the dawn of programming languages: dynamic scope. Dynamically looking up the stack for variable bindings is thread-safe, exception-safe and remarkably less error-prone than global state. It has also quite rightfully been mostly abandoned in favour of lexical scope.

But what if you could get all the great properties of dynamic scope with lexically scoped variables? That is what Racket achieves with parameters. Racket parameters
  • are explicitly declared as such, so you wouldn’t confuse them with regular variables
  • hold their value within blocks where they’re defined, called parameterize blocks
  • can be nested safely and work as you would expect them to
  • importantly for a Scheme dialect, don’t interfere with tail call optimization
  • are, by their very structure, thread-safe and exception-safe — it is always beautiful to see the semantics you want be a natural, obvious consequence of the syntax you’ve created
So the next time you’re tempted to use a global variable to solve a problem, don’t blame yourself; instead, blame the language you’re using for providing insufficient abstractions.