Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Clojure by
edited by

I have been using the following function a lot:

 (defn set-var-root [v value]
   (alter-var-root v (constantly value)))

And I have seen this pattern a lot in other code too. It is like an idiom if I understand correctly.
Why is it not in clojure.core?

Might it be a good addition and if not, why not?

I have asked in clojurians slack and was pointed out to

(.bindRoot avar value)

Which I didn't know before also. But again the question why is it not part of the public API? Is there something wrong with using vars like this?

To give some context, I am developing a game engine and on app start I am setting global vars for input, graphics context, etc.

https://github.com/damn/gdl/blob/main/src/gdl/game.clj#L13

(defn- load-gdx-globals []
  (set-var-root #'gdl.app/app           Gdx/app)
  (set-var-root #'gdl.files/files       Gdx/files)
  (set-var-root #'gdl.graphics/graphics Gdx/graphics)
  (set-var-root #'gdl.input/input       Gdx/input))
by
I am using `.bindRoot` now, and can delete my custom function `set-var-root`.

I think that's the best approach.
by
Using the non-public, non-portable implementation api seems like the worst approach tbh.

1 Answer

+2 votes
by

As with all reference types, you should generally be creating new values by functionally applying a functional transformation to the old value (ie alter-var-root).

But really, because vars are global, you should prefer to use other mechanisms to hold state, either passing a stateful reference (like an atom) around, or in some relatively rare cases by putting a stateful reference in a var (but the fewer the better imo, ideally 0).

by
Yes, this is not in clojure.core because it is not something that you should be doing in general, as explained by this answer. State which changes after compilation really belongs in something protected like an atom, and the update semantics are a pure function which transforms the old state to the new state (which, for atoms, uses `swap!`).
by
In the case of my project (a game engine, see https://github.com/damn/Fullmoon) there are a lot of values which are initialized only once after application start.

For example viewport, spritebatch, camera, textures and sounds are loaded.
They have a well defined lifecycle (https://libgdx.com/wiki/app/the-life-cycle).
I am using the idiom `(alter-var-root avar (constantly value))` as I do not need to dispose of the resources (this is already done on application shutdown.

Those vars are only re-assigned in development mode when using clojure.tools.namespace refresh.

I have done a github search and even found an article on the idiom: https://github.com/damn/Fullmoon

So it is really widespread. I agree it should not be used by beginners! But it was also confusing for me that such a function does not exist. We also have `reset!` for atoms and `set!` for dynamic vars.
by
Data that is loaded after application startup and never changed, feels like something that should not even be in a global Var -- it feels like it should be immutable and passed to the "program" once it is built in -main (or some initialization function).
...