Reputation: 382
I'm starting on functional programming now and I'm getting very crazy of working without variables.
Every tutorial that I read says that it isn't cool redefine a variable but I don't know how to solve my actual problem without saving the state on a variable.
For example: I'm working on a API and I want to keep the values throught the requests. Lets say that I have a end-point that add a person
, and I have an list of persons
, I would like to redefine
or alter the value of my persons
list adding the new person
. How can I do this?
Is there ok to use var-set
, alter-var-root
or conj!
?
(for the api i'm using compojure-api
and each person
would be a Hash
)
Upvotes: 1
Views: 1159
Reputation: 45826
You'll likely need a mutable state somewhere in a large application, but one isn't necessary in all cases.
I'm not familiar with compojure, but here's a small example using immutability that might be able to give you a better idea:
(loop [requests []
people []
(let [request (receive-request)]
; Use requests/people
; Then loop again with updated lists
(recur (conj requests request)
(conj people (make-person request))))])
I'm using hypothetical receive-request
and make-person
functions here.
The loop
creates a couple bindings, and updates them at each recur
. This is an easy way to "redefine a variable". This is comparable to pure recursion, where you don't mutate the end result at any point, you just change what value gets passed onto the next iteration.
Of course, this is super simple, and impractical since you're just receiving one request at a time. If you're receiving requests from multiple threads at the same time, this would be a justifiable case for an atom:
(defn listen [result-atom]
(Thread.
(fn []
(while true ; Infinite listener for simplicity
(let [request (receive-request)]
(swap! result-atom #(conj % (make-person request))))))))
(defn listen-all []
(let [result-atom (atom [])]
(listen result-atom)
(listen result-atom)))
; result-atom now holds an updating list of people that you can do stuff with
swap!
mutates the atom by conj
oining onto the list it holds. The list inside the atom isn't mutated, it was just replaced by a modified version of itself. Anyone holding onto a reference to the old list of people would be unaffected by the call to swap!
.
A better approach would be to use a library like core/async, but that's getting away from the question.
The point is, you may need to use a mutable variable somewhere, but the need for them is a lot less than you're used to. In most cases, almost everything can be done using immutability like in the first example.
Upvotes: 2
Reputation: 240
Clojure differentiates values from identity. You can use atoms to manage the state in your compojure application.
(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value
You can find more in docs:
https://clojure.org/reference/atoms
https://clojure.org/reference/data_structures
https://clojuredocs.org/clojure.core/atom
Upvotes: 6