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.
Related
New to clojure. Trying to solve the following problem with a java background. I need to transform table to a hash-map that maps products to all the cities that sell the product. So the output should be.
{"Pencil": ("Oshawa" "Toronto")
"Bread": ("Ottawa" "Oshawa" "Toronto")}
(def table [
{:product "Pencil"
:city "Toronto"
:year "2010"
:sales "2653.00"}
{:product "Pencil"
:city "Oshawa"
:year "2010"
:sales "525.00"}
{:product "Bread"
:city "Toronto"
:year "2010"
:sales "136,264.00"}
{:product "Bread"
:city "Oshawa"
:year "nil"
:sales "242,634.00"}
{:product "Bread"
:city "Ottawa"
:year "2011"
:sales "426,164.00"}])
This is what I have so far. I write this code into the repl.
(let [product-cities {}]
(for [row table]
(if (= (contains? product-cities (keyword (row :product))) true)
(println "YAMON") ;;To do after. Add city to product if statement is true
(into product-cities {(keyword (row :product)) (str (row :city))}))))
However, the outcome is the following:
({:Pencil "Toronto"}
{:Pencil "Oshawa"}
{:Bread "Toronto"}
{:Bread "Oshawa"}
{:Bread "Ottawa"})
My if statement keeps returning false. I see that there are semi-circle brackets around the many hash-maps. I can't figure out why it's not returning one hashmap and why there are many hashmap? Thanks
EDIT:
QUESTION 2:
Transform table to a hash-map that maps products to the city that has the highest sale. For example, the output should look like:
{"Pencil": "Toronto"
"Bread": "Ottawa"}
I think a different strategy is needed than building up a value but here's what I'm thinking:
(reduce (fn [product-cities {:keys [product city sales]}]
(update-in product-cities [product] (fnil conj []) {(keyword city) sales}))
{}
table)
This produces the following output:
{"Bread"
[{:Toronto "136,264.00"}
{:Oshawa "242,634.00"}
{:Ottawa "426,164.00"}],
"Pencil"
[{:Toronto "2653.00"}
{:Oshawa "525.00"}]}
I could then use the reduce function again but only add the city with max sales. I don't think this is the most efficient way.
You seem to have some misconceptions about how clojure works. Coming from java it can be hard to know how to do stuff, just because it's so different. This small problem serves nicely as an introduction to how to build up a value, and I'll try to explain each part of the solution.
Immutability
It's a common pattern in java to define a variable that will hold the final result, then loop through something while adding to that variable.
That's what you're trying to do with your product-cities local. When you define a local with let in clojure it never changes, so to build up a value you need another pattern.
Adding something to a map
First let's take a look at how to "add something to a map". In clojure what you actually do is make a new map with the thing added. The old map doesn't change. We still sometimes phrase it as adding to a map, but that's just shorthand for "make a new map with the thing added".
assoc takes a map, a key and a value and returns a new map with the value added at the key. If there's already a value there it will be overwritten. We want multiple things for each key, so it's not the right thing in this case.
update is similar, but it takes a map, a key, a function and optionally arguments to that function. It will call the function with the value that's already at key as the first argument and (if present) the arguments supplied. The returned map will have the return value of the function as the new value at key. Some examples might make this clearer.
;; The function - is called with the old value of :foo and the argument supplied
;; (- 10 3)
(update {:foo 10} :foo - 3) ;=> {:foo 7}
If there's nothing already at key, the function will be called with nil as the first argument. That's what nil means, nothing.
(update {} :foo + 5) ;=> Null pointer exception. Same as (+ nil 5)
Null pointers are no good. There's a trick for avoiding them. fnil is a higher order function that takes a function and arguments. It returns a new function that will substitute a nil argument for the arguments supplied.
;; The nil here is substituted with 0, so no NPE
((fnil + 0) nil 5) ;=> 5
;; If the arg is not nil, the zero is not used.
((fnil + 0) 5 5) ;=> 10
;; So now we can update the value at :foo regardless of whether it's already there.
(update {} :foo (fnil + 0) 5) ;=> {:foo 5}
conj adds something to a collection. If that collection is a vector, it adds it at the end.
(conj [] :foo) ;=> :foo
(conj [:foo] :bar) ;=> [:foo :bar]
To add things to the map we can combine these:
(update {} "product" (fnil conj []) "city") ;=> {"product ["city"]"}
(update {"product" ["city"]} "product" (fnil conj []) "another city")
;;=> {"product" ["city" "another city"]}
Building up a value
We need to do some looping somehow. A for in clojure is a list comprehension however, and not a for loop. It will return a sequence of things, so it's not the right thing to use when you want to build up a value.
One way to do it is with loop.
With a loop you define binding names paired with their initial value. The bindings in loop can be thought of as "the things that are going to change during the loop".
One difference between loop and traditional loops is that to break out of the loop you simply don't do anything, and you need to specifically use recur to keep looping.
;; the bindings are pairs of names and initial values
(loop [product-cities {} ; This is the accumulator that we're gonna build up.
rows table] ; and these are the rows, we're gonna go through them one by one.
;; Here's the base case, if there are no rows left we return the accumulator.
(if (empty? rows)
product-cities
;; If there are rows left, we need to add to the accumulator.
(let [row (first rows)
city (:city row)
product (:product row)
new-accumulator (update product-cities product (fnil conj []) city)]
;; recur takes as many arguments as the pairs we defined
;; and "jumps" back to the loop
(recur new-accumulator (rest rows)))))
;;=> {"Pencil" ["Toronto" "Oshawa"], "Bread" ["Toronto" "Oshawa" "Ottawa"]}
This can be made nicer with some destructuring.
(loop [product-cities {}
[{:keys [city product] :as row} & rows] table]
(if (nil? row)
product-cities
(recur (update product-cities product (fnil conj []) city) rows)))
;;=> {"Pencil" ["Toronto" "Oshawa"], "Bread" ["Toronto" "Oshawa" "Ottawa"]}
loop is not much used in normal clojure though. It's too general and usually you want something more specific. Like in this case, you want to build up a value while looping through every thing in a sequence of things. That's what reduce does.
Reduce takes three arguments, a function, an initial value and a collection. The function, called a "reducing function" takes two arguments; the accumulated value so far and an item. It is called once for each item in the collection.
So the final implementation becomes:
(reduce (fn [product-cities {:keys [product city]}]
(update product-cities product (fnil conj []) city))
{}
table)
Edit:
About your comment on the other answer. update was added in clojure 1.7.0 so you're presumable on an older version. You can use update-in instead (though you should consider upgrading). It's called in exactly the same way except the key is in a vector.
(reduce (fn [product-cities {:keys [product city]}]
(update-in product-cities [product] (fnil conj []) city))
{}
table)
You need to go through the table one by one and accumulate (conj) the cities under the product:
(reduce
(fn [acc {:keys [product city]}]
(update acc product conj city))
{}
table)
;; => {"Pencil" ("Oshawa" "Toronto"), "Bread" ("Ottawa" "Oshawa" "Toronto")}
The use of the updating function (conj in this case) can be a bit tricky, so here is an alternative formation of the update:
(update acc product (fn [cities]
(conj cities city)))
Instead of {} I started out with:
{"Pencil" []
"Bread" []}
That might make it easier to see that for each entry in the table the update is updating the product key ("Pencil" or "Bread") by putting the latest city on the end (that's what conj does) of the sequence. When it was working I just replaced with {}, using the fact that update will insert a new key if one is not there.
I think of for as being generative. It takes a sequence as input and at each step generates a new thing - hence you end up with a sequence of new things. There is no updating of product-cities possible with generation.
reduce is more useful for what you want to do as you get an 'accumulator' that can be slightly modified at each step. Actually you are creating a new 'accumulator' each time, by modifying the one that is passed in, so in reality you are not modifying anything at all: Clojure being a functional language, its all about creating new things.
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
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.
I'm trying to read metadata for a collection of functions in Clojure, but the var or reader special forms do not work unless they are directly dealing with the symbol.
; this works
(var my-fn)
; this doesn't
(defn val-it [x] (var x))
(val-it my-fn)
Is there any way to get this to work within the scope of another function?
resolve returns the Var or class object corresponding the given symbol in the context of the current namespace. ns-resolve allows you to specify which namespace to resolve the symbol in.
(resolve 'my-fn)
;= #'some.ns/my-fn
If the symbol cannot be resolved to a Var, nil is returned.
(var my-fn) does deal directly with the symbol because it is a special form (the reader receives the form unevaluated).
The metadata you want to read is stored in the var object, not in the function object.
Thus your goal, to read metadata from a list of function objects, is only achievable by traversing all existing vars and comparing their value by equality. I'd only recommend it if the function objects are the only way to start.
(defn meta-of
"Returns a hashmap mapping the function objects in fn-objs to
a set of metadata of vars containing it."
[fn-objs]
(let [fn-objs (set fn-objs)]
(reduce (fn [acc ns]
(reduce (fn [acc var]
(let [val (var-get var)]
(cond-> acc
(contains? fn-objs val)
(update-in [val] (fnil conj #{}) (meta var)))))
acc
(vals (ns-interns ns)))) {} (all-ns))))
(def foo inc) ;; Now there are two vars that have the inc function as their value
(meta-of [inc])
{#<core$inc clojure.core$inc#66d7e31d> ;; <- function object
#{{:ns #<Namespace clojure.core>, ;; <- metadata in clojure.core namespace
:name inc,
:file "clojure/core.clj",
:column 1,
:line 881,
:arglists ([x]),
:added "1.2",
:inline #<core$inc__inliner clojure.core$inc__inliner#24f87069>,
:doc
"Returns a number one greater than num. Does not auto-promote\n longs, will throw on overflow. See also: inc'"}
{:ns #<Namespace user>, ;; <- metadata in user namespace
:name foo,
:file "/tmp/form-init1078564431656334911.clj",
:column 1,
:line 1}}}
I have a simple record definition, for example
(defrecord User [name email place])
What is the best way to make a record having it's values in a sequence
(def my-values ["John" "john#example.com" "Dreamland"])
I hoped for something like
(apply User. my-values)
but that won't work. I ended up doing:
(defn make-user [v]
(User. (nth v 0) (nth v 1) (nth v 2)))
But I'm sensing there is some better way for achieving this...
Warning: works only for literal sequables! (see Mihał's comment)
Try this macro:
(defmacro instantiate [klass values]
`(new ~klass ~#values))
If you expand it with:
(macroexpand '(instantiate User ["John" "john#example.com" "Dreamland"]))
you'll get this:
(new User "John" "john#example.com" "Dreamland")
which is basically what you need.
And you can use it for instantiating other record types, or Java classes. Basically, this is just a class constructor that takes a one sequence of parameters instead of many parameters.
the defrecord function creates a compiled class with some immutable fields in it. it's not a proper clojure functions (ie: not a class that implements iFn). If you want to call it's constructor with apply (which expects an iFun) you need to wrap it in an anonymous function so apply will be able to digest it.
(apply #(User. %1 %2 %3 %4) my-values)
it's closer to what you started with though your approach of defining a constructor with a good descriptive name has its own charm :)
from the API:
Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.
Writing your own constructor function is probably the way to go. As Arthur Ulfeldt said, you then have a function you can use as a function (e.g. with apply) rather than a Java-interop constructor call.
With your own constructor function you can also do argument validation or supply default arguments. You gain another level of abstraction to work with; you can define make-user to return a hash-map for quick development, and if you later decide to change to records, you can do so without breaking everything. You can write constructors with multiple arities, or that take keyword arguments, or do any number of other things.
(defn- default-user [name]
(str (.toLowerCase name) "#example.com"))
(defn make-user
([name] (make-user name nil nil))
([name place] (make-user name nil place))
([name user place]
(when-not name
(throw (Exception. "Required argument `name` missing/empty.")))
(let [user (or user (default-user name))]
(User. name user place))))
(defn make-user-keyword-args [& {:keys [name user place]}]
(make-user name user place))
(defn make-user-from-hashmap [args]
(apply make-user (map args [:name :user :place])))
user> (apply make-user ["John" "john#example.com" "Somewhere"])
#:user.User{:name "John", :email "john#example.com", :place "Somewhere"}
user> (make-user "John")
#:user.User{:name "John", :email "john#example.com", :place nil}
user> (make-user-keyword-args :place "Somewhere" :name "John")
#:user.User{:name "John", :email "john#example.com", :place "Somewhere"}
user> (make-user-from-hashmap {:user "foo"})
; Evaluation aborted.
; java.lang.Exception: Required argument `name` missing/empty.
One simple thing you can do is to make use of destructuring.
(defn make-user [[name email place]]
(User. name email place))
Then you can just call it like this
(make-user ["John" "John#example.com" "Dreamland"])
Update for Clojure 1.4
defrecord now defines ->User and map->User thus following in Goran's footstaps, one can now
(defmacro instantiate [rec args] `(apply ~(symbol (str "->" rec)) ~args))
which also works with non-literal sequences as in (instantiate User my-values).
Alternatively, along the lines of map->User one can define a function seq->User
(defmacro def-seq-> [rec] `(defn ~(symbol (str "seq->" rec)) [arg#] (apply ~(symbol (str "->" rec)) arg#)))
(def-seq-> User)
which will allow (seq->User my-values).
The idiomatic way to call a Record constructor is with the Clojure symbol ->MyRecord and that works just fine with apply.
(def my-values ["John" "john#example.com" "Dreamland"])
(defrecord User [name email place])
(apply ->User my-values)
; => #my-ns.User{:name "John",
:email "john#example.com",
:place "Dreamland"}