3

Until recently I was a huge fan of global variables because they are simple and allow building solutions to problems quickly.

At one point I had to make a slight change to my application: it needed to fetch data from multiple sources.

I had a couple of simple global functions like RetrieveUser(id string) and FindUserByName(name string) that used a global db object. It turned out that in order to do that, I had to re-write the database logic and update all db function calls.

I now have a UsersRepo struct with a db field that can fetch data from anywhere.

This is pretty obvious to most programmers, however for some reason I never understood the benefits of this approach until now.

So I decided to go further and research other ways to improve my programs.

I came across this article:

https://peter.bourgon.org/blog/2017/06/09/theory-of-modern-go.html

One of the points it makes is that global state is evil and unnecessary, and all dependencies should be passed as function arguments.

What do we replace globals with? Global "context" objects. Here's an example of Go's stdlib OpenGL library moving from globals to a Context:

https://go-review.googlesource.com/c/mobile/+/10396

My question is about these context objects.

For example, in a game instead of a global pos variable that stores the position of the player we would have a Game context object that we create once and pass everywhere:

// Using globals
var (
  pos int
  velocity int
)

func move_player() {
    pos += velocity
}


// Using global context object
type Game struct {
    pos int
    velocity int
    ...
}

func (game *Game) move_player() {
    game.pos += game.velocity
}

But I feel like it's almost the same thing. We are modifying global state in move_player() and it's not obvious the function is doing it.

The only advantage I can see is that we can now have multiple Game objects in case we need to solve a problem similar to what I described in the beginning.

I feel like this is a limitation of all imperative languages. It's very easy to write functions with side effects, and it's especially bad in Go where unlike C/C++ you don't have const pointers/references, so if you are passing a pointer to a an object, you can never be sure it won't be modified.

So if we were designing a new language, other than going full Functional Programming, what would be the way to solve this?

Or it it simply unavoidable in an imperative language?

I mean we could make the move_player() function pure

   func (game *Game) update_pos() int { return game.pos + game.velocity }

but the position still has to be changed somewhere.

Sorry if this doesn't make sense. This is something I've been thinking a lot about recently and couldn't come up with an answer.

Thanks.

2
  • 2
    Do you want to design a programming language with or without global variables or do you want to change your programming style to use fewer global variables or not? Jul 14, 2018 at 10:24
  • A functional language should implement copy-on-write (COW) to reduce the amount of copying. This would give it speeds comparable to using globals. Jul 14, 2018 at 11:35

1 Answer 1

7

You are mixing two things here: accessability and mutability of variables.

Of course is func (game *Game) move_player() going to mutate the state of an object. That's the whole point of the function. Motion means changing position. The point of injecting the object to be mutatated is that only this object is mutated.

I feel like this is a limitation of all imperative languages. It's very easy to write functions with side effects, and it's especially bad in Go where unlike C/C++ you don't have const pointers/references, so if you are passing a pointer to a an object, you can never be sure it won't be modified.

The real problem is that Go doesn't have e.g. classes like C++ with private members to encapsulate the state of an object and prevent access to it from outside the class. This way it is more obvious that methods might change the state of the object, because there's no other way of getting access to it. In fact, if some methods wouldn't access the private state, they should probably be static.

In your example, you'd have a Player class and calling its move() method would only (and could only) mutate the state of that object i.e. its private variable. That's perferctly fine because that's what this method is supposed to do.

There's no need to design a new language. A lot languages out there have features that solve the problems that you have with Go.

2
  • 1
    Pure functional languages are a delusion. It's easy to get around their limitations. state = foo( state ) where state contains all the data of the program. Now, everything is a global. Jul 31, 2018 at 11:30
  • 3
    @shawnhcorey Just because you can defeat a mechanism, doesn't make it useless. Following your logic, type systems (can be subverted by abuse of the top type, e.g. java.lang.Object, access control (can be subverted with reflection), immutability (can be subverted with raw memory access), object orientation (can be subverted by only ever using static methods, no poly morphism, no instance data), structured programming (can be subverted by goto), etc. are all "a delusion." That's clearly non-sense.
    – Alexander
    Sep 22, 2019 at 1:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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