clojure

In Clojure, how to change (def) a variable value inside a function


So, I've just started learning Clojure and I'm now facing a problem that I couldn't google myself out of.

Here is my code atm:

=> (def a "asd")
=> a
"asd"
=> (defn bla [entry_value] (def entry_value "qwe"))
=> (bla a)
#'exercise.core/entry_value
=> a
"asd"
=> entry_value
"qwe"

What I really wanted was for a to have "qwe" as value. Is it possible?

Thanks for the help!


Solution

  • Yes, technically it is possible with the help of alter-var-root function like this

    => (defn bla [entry-value] (alter-var-root entry-value (fn [_] "qwe")))
    => #'user/bla
    => (def a "asd")
    => #'user/a
    => (bla #'a)
    => "qwe"
    => a
    => "qwe"
    

    But this is not how Vars (things definded with def) are supposed to work in Clojure. def is used to declare a binding in Clojure, not a variable. Binding means that you bind some value to some name and it does not change in the future e.g. that binding is immutable. Other functions in your program may relay on that immutability, so please do not use alter-var-root or other methods of changing bindings in your code.

    If you really need some kind of variable with mutable state - please use atoms.

    I would encourage you to read "Clojure for brave and true" which contains very good and concise explanations on the topic.

    About def here under "Naming values with Def" section.

    Atoms, Vars and state management are discussed here.

    P.S. You can test the code above using this online repl.