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.