Reputation: 1410
I have a use case where I want to update one of my map
type variables inside a method call. To demonstrate here is a code snippet,
(defn func [mymap]
(conj mymap [1 2])) ;update mymap variable here such that changes are persistent after the method returns
(let [mymap {}
_ (func mymap)] (println mymap))
which outputs {}
because I think a new map is created with the conj
function. How do I update the mymap
variable in func
such that the output of the above program will be {1 2}
?
If it is not possible in Clojure, how are such use cases handled in general?
Upvotes: 0
Views: 93
Reputation: 37008
Clojure uses immutable data types by default. This means, you cannot mutate the data in place like you are used to from many other programming languages.
The functional approach here is to use the result from the conj
(the
last statement inside a defn
is it's return value).
(let [mymap {}
result (func mymap)]
(println result))
The longer you can keep up with pure functions on immutable data the easier your life will be; reasoning about your programs and testing them becomes a lot easier.
There is of course the option to use mutable data classes from Java, but don't use them unless you really have to.
And since nearly all programs need some state, there are also atom
:s
def
everywhere, atom
everywhere are the next best "trap" beginners run into.Upvotes: 1
Reputation: 29958
Many choices. Simplest is to rebind the mymap
variable. Consider:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn doit [mymap]
(into mymap {:b 42}))
(dotest
(let [m {:a 1}
m2 (doit m)]
(spyx m2)))
m2 => {:a 1, :b 42}
and we get what we expect.
Update the code to reuse the name m
:
(dotest
(let [m {:a 1}
m (doit m)]
(spyx m)))
m => {:a 1, :b 42}
Here the 2nd usage of m
creates a separate variable that shadows the first m
. This works great and people do it accidentally all the time without even realizing it.
If you want to copy the behavior of Java, you need a Clojure atom
to create a mutable storage location.
(dotest
(let [m-atom (atom {:a 1})]
(swap! m-atom doit)
(spyx @m-atom)))
(deref m-atom) => {:a 1, :b 42}
Here swap! applies the function doit
to the contents
of m-atom
, the puts the results as the new contents.
We need the @m-atom
or (deref m-atom)
to pull out the contents of the atom for printing.
The above convenience functions can be found here. I also have some great documentation references in this template project. Be especially sure to study the Clojure Cheatsheet daily.
Upvotes: 1