What is the difference between using swap! and reset! in Clojure functions? I have seen from the clojure.core docs that they are used to change the value of an atom but I am not sure when to use swap! and when to use reset!.
What circumstances would you use swap! and which circumstances would you use reset!?
[:input {:type "text"
:value #time-color
:on-change #(reset! time-color (-> % .-target .-value))}]
The above code is an example of using reset! for a button
[:input.form-control
{:type :text
:name :ric
:on-change #(swap! fields assoc :ric (-> % .-target .-value))
:value (:ric #fields)}]
And this button uses swap!
Are swap! and reset! interchangeable?
Thanks
swap! uses a function to modify the value of the atom. You will usually use swap! when the current value of the atom matters. For example, incrementing a value depends on the current value, so you would use the inc function.
reset! simply sets the value of the atom to some new value. You will usually use this when you just want to set the value without caring what the current value is.
(def x (atom 0))
(swap! x inc) ; #x is now 1
(reset! x 100) ; #x is now 100
(swap! x inc) ; #x is now 101
Related
I am trying to understund this piece of code from Web Development With Clojure book. Its about clojure script:
(defn message-form []
(let [fields (atom {})]
(fn []
[:div.content
[:div.form-group
[:p "Name:"
[:input.form-control
{:type :text
:name :name
:on-change #(swap! fields assoc :name (-> % .-target .-value))
:value (:name #fields)}]]]
[:p "Message:"
[:textarea.form-control
{:rows 4
:cols 50
:name :message
:on-change #(swap! fields assoc :message (-> % .-target .-value))}
(:message #fields)]]
[:input.btn.btn-primary {:type :submit :value "comment"}]])))
Can anybody explain this part:
#(swap! fields assoc :name (-> % .-target .-value)
especially this:
...(-> % .-target .-value)
[:input {:on-change #(swap! fields assoc :name (-> % .-target .-value)}]
This defines an input and a function that will be called each time the user changes the input field, i. e. types in it. The function will be called with the change event object as argument, which is then bound to %. The event target is the input field DOM element, which has as value its text content. The function then changes fields to contain as :name that content.
The #(…) and the % belong together: #(…) creates an anonymous function and % is the name of its parameter.
The -> is a macro that inverts the usual lispy prefix composition of forms into something resembling postfix composition or "piping": (-> % .-target .-value) is expanded to (.-value (.-target %)).
The .-value notation is JavaScript interop in ClojureScript. It denotes the access of a field "value" of a JavaScript object; (.-value foo) in ClojureScript is essentially the same as writing foo.value or foo["value"] in JavaScript.
In your question swap! is taking an atom, a function (assoc) that updates the value the atom stores and subsequent arguments that are given to the function.
Here is another way of achieving the same result:
#(swap! fields (fn [prev] (assoc prev :name (-> % .-target .-value)))
See that :name and (-> % .-target .-value) would be the two subsequent arguments in the swap! invocation of your question, where apply is used internally to apply these arguments to assoc. Here there is just an update function - no subsequent arguments.
As you can see prev is the internal value of the atom that we are going to change and swap! just takes an 'updating' function, which returns the next value for swap! to store.
As far as (-> % .-target .-value) goes, remember you are already in the reader macro, so % is the first argument to it. The -> threading macro threads into the first argument of each following function, so % is given to .-target and then the result of that is given to .-value, giving us: (.-value (.-target %)).
:name in the map stored at fields is being set to the target value of some value that is being provided!
Going out a little wider % is a JavaScript event that is provided whenever the text input field is changed - basically whenever the user types text. You rarely want the whole of the event - just the text that has been typed, which is where getting the target of the event (as opposed to the source) and then that target's value, comes in. The .- part means you are getting a JavaScript property - this is 'interop', as opposed to normal ClojureScript syntax.
#() is a reader macro. It is a short form for an anonymous function (fn [args] ...), where % is the first argument of this function.
In this particular case #(swap! fields assoc :name (-> % .-target .-value) is equal to (fn [X] (swap! fields assoc :name (-> X .-target .-value))
-> is a threading macro and (-> X .-target .-value) is equivalent to (.-value (.-target X)).
(.-target X) means get property target of object X.
So you define an anonymous function which receives single argument. This function will change the fields atom so that its key :name will be set to the value of the property value of the object in property target of the object X.
I am new to Clojure.
I have used to change value of identifier using swap! and reset!.
reset!
(def item (atom "Apple"))
user=> #item
Out Put ;;=> "Apple"
(reset! item "Grapes")
user=> #item
Out Put ;;=> "Grapes"
swap!
(def item (atom "Apple"))
user=> #item
Out Put ;;=> "Apple"
(swap! item (#(str %) "PineApple"))
Out Put ;;=> ClassCastException java.lang.String cannot be cast to clojure.lang.IFn
How can I change value of item by using swap!?
(swap! item (fn [old] "PineApple"))
or:
(swap! item (fn [_] "PineApple"))
But as you are discarding the input, reset! is better here:
(reset! item "PineApple")
As per swap! syntax (swap! atom f). swap should require function.
So I just tried to solve the issue in this way.
(swap! item (fn[s] "Banana"))
Output ;;=> Banana
I'm very new in Clojure. I'm learning with help from Clojure Koans. I found an answer with code below:
(= ["Real Jerry" "Bizarro Jerry"]
(do
(dosync
(ref-set the-world {})
(alter the-world assoc :jerry "Real Jerry")
(alter bizarro-world assoc :jerry "Bizarro Jerry")
(vec (map #(:jerry #%) [the-world bizarro-world]))))))
from: https://github.com/viebel/clojure-koans/blob/master/src/koans/16_refs.clj#L42
It's pretty unfriendly for Google to search like "Clojure #%". So I get nothing from Internet.
How does it works for the function "#(:jerry #%)"?
And the code below is the answer from me, but it doesn't work.
(= ["Real Jerry" "Bizarro Jerry"]
(do
(dosync
(ref-set the-world {})
(alter the-world assoc :jerry "Real Jerry")
(alter bizarro-world assoc :jerry "Bizarro Jerry")
(vec (map (fn [x] (:jerry x)) [the-world bizarro-world]))
)))
#( ...) is a reader macro for anonymous function where % means the first argument passed to the function. For example:
#(println %)
is equivalent to:
(fn [x] (println x))
# is a reader macro for deref so again:
#some-variable
is the same as:
(deref some-variable)
and is used to dereference a current value from one of the ref types.
Thus #(:jerry #%) is an anonymous function which when applied to a ref (e.g. an atom) will deref its current value and use it as an argument to call :jerry keyword as a function with the value.
the-world and bizarro-world are "derefable", which means that you can use # in front to get their value.
You are using an anonymous function, indicated by #( ). In an anonymous function, the percent sign % indicates the argument to the function.
So #% means, "dereference the argument to this function."
:jerry is a keyword used as a function, which gets the value associated with the key :jerry in the map.
For example:
(def coll [(ref {:jerry 21})
(ref {:jerry 42})])
=> #'user/coll
(map #(:jerry #%) coll)
=> (21 42)
Besides, you could find other "weird" symbols in clojure here .
https://yobriefca.se/blog/2014/05/19/the-weird-and-wonderful-characters-of-clojure/
I am working on a simple web-app using clojurescript and reagent. I would like to create a simple "tab" component, which will contain (for starters) a text-input component.
The app has 2 tabs and the user has the option to choose a tab and I want to "preserve" the values in each of these two tabs.
Here's the code:
(defn atom-input [value]
[:input {:type "text"
:value #value
:on-change #(reset! value (-> % .-target .-value))}])
(defn simple-tab [index]
(let [pg-index (atom 1)
a (atom 0)]
(fn []
[:div
[:h4 (str "index: " #index)]
[atom-input a]])))
(defn main-page []
(let [index (atom 0)]
[:div.container
[:div.row
[:button {:on-click (fn [] (reset! index 0))} "select tab 1"]
[:button {:on-click (fn [] (reset! index 1))} "select tab 2"]]
[:div.row
[simple-tab index]]]))
(defn ^:export run []
(reagent/render-component
(fn [] [main-page])
(.-body js/document)))
The problem is that when I switch the tab, the components share the values of the input field - what am I please doing wrong here?
Thank you so much for your help!
The problem is you're passing a (atom 0) to the atom-input control: [atom-input a].
This caused the same atom value to be shared between your tabs.
If you don't want to share the value, you'll need change a to a map: a (atom {}) and pass the map and the index to atom-input, e.g.:
(defn atom-input [value index]
[:input {:type "text"
:value (or (get #value index) "")
:on-change #(swap! value assoc index (-> % .-target .-value))}])
(defn simple-tab [index]
(let [pg-index (atom 1)
a (atom {})]
(fn []
[:div
[:h4 (str "index: " #index)]
[atom-input a #index]])))
A better approach, IMHO, is to use cursor so you don't need to pass the index & the whole map to atom-input, e.g.:
(defn atom-input [value]
[:input {:type "text"
:value (or #value "")
:on-change #(reset! value (-> % .-target .-value))}])
(defn simple-tab [index]
(let [pg-index (atom 1)
a (atom {})]
(fn []
[:div
[:h4 (str "index: " #index)]
[atom-input (reagent/cursor [#index] a)]])))
I think there are a couple of problems here because you are mixing up application data (state) and display logic data (i.e. DOM). If you keep the two things distinct i.e. maintain your application state in one atom and data relating to component display in another, then things may be a bit cleaner.
Your simple-tab component does not need to know anything about the tab state. It just needs to know about the app state i.e. the value entered/stored via atom-input. Therefore, rather than passing it index, pass it the atom you want it to use. this would require some higher level logic to determine the call. for example, if you had a number of tabs, you might have something like
(condp = #index
0 [simple-tab tab0-atom]
1 [simple-tab tab1-atom]
...
n [simple-tab tabn-atom])
or you could modify simple-tab so that the value passed in i.e. index value is used as a key into the app-state - a cursor would be easiest I think i.e.
(def app-state (r/atom {:tabs {0 nil 1 nil}}})
(defn simple-tab [index]
(let [val-cur (r/cursor app-state [:tabs index])]
[atom-input val-cur]))
You are using a form-2 component, that is, a component that returns a function.
More details here : https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#form-2--a-function-returning-a-function
When doing so, only the returned function is called so your atom-inputs are sharing the same atom.
Also, you should use the same argument in the inner function
(defn simple-tab [index]
(let [pg-index (atom 1)
a (atom {})]
(fn [index]
[:div
[:h4 (str "index: " #index)]
[atom-input a #index]])))
In your case, you are passing an atom so this is not important but you could have error in the future if you forgot this.
Concerning the broader architecture, I would advise you to use a single global atom. Try to have as much as possible state in this atom and avoid component local state so this is easier to reason about.
You could also name your tabs like :product :users and use a multimethod to render the correct tab based on the selected one. This is easier to read and easier to add new tabs in the future.
Is there an easier/more idiomatic way to store/swap keywords in atoms than:
(def a (atom :a))
(defn change-a [new-kw] (swap! a (fn [_] new-kw)))
No use case as yet, just wondering. It's also entirely possible that I'm missing something, and this kind of thing shouldn't happen/never actually happens in the wild because [some other pattern] is a much better solution.
atoms can be either swapped (CAS) or reset to a different value. Swapping is done with a modifying function e.g. :
user=> (swap! (atom 41) inc)
42
Notice that a previous (e.g. current) value of an atom is taken into a count when swapping.
Reseting a value of an atom is done "without regard for the current value":
user=> (reset! (atom 41) 42)
42
In your case it could be used as:
(reset! a :b)
In case you'd like to reset a value of an atom keeping the CAS semantics, you can compare-and-set! it:
user=> (def a (atom 41))
#'user/a
user=> (compare-and-set! a #a 42)
true
user=> #a
42