I created the ref "people" below, and want the function delete-person to delete an item from the data structure within a transaction.
(defrecord Person [name favorite-color])
(def people (ref ()))
(defn add-person [new-person]
(dosync
(alter people conj new-person)))
(add-person (Person. "Joe" "Red"))
(add-person (Person. "Sam" "Blue"))
;; how do I make this function work?
;; would #people (destructured) have to be the second argument to filter?
(defn delete-person [name-to-delete]
"Delete named person from ref"
(dosync
(alter people filter #(not= (:name %) name-to-delete))))
(delete-person "Joe")
IllegalArgumentException Don't know how to create ISeq from:
user$delete_person$fn__1407$fn__1408 clojure.lang.RT.seqFrom (RT.java:542)
The function below works because I filter on the destructured ref, but how do I do it in a transaction to mutate the data?
(filter #(not= (:name %) "Sam") #people)
=> (#user.Person{:name "Joe", :favorite-color "Red"})
As there error says, you're trying to iterate a function. This is coming about because when you write:
(alter people filter #(not= (:name %) name-to-delete))
The unwrapped people becomes the first argument to filter, not the last.
You'll need to use an full fn, or use partial:
(alter people
(fn [ps] (filter #(not= (:name %) name-to-delete) ps)))
Or
(alter people
(partial filter #(not= (:name %) name-to-delete)))
These make alter pass the unwrapped people as the last argument to filter, instead of implicitly as the first.
I'll note though:
As #cfrick brought up in the comments, using lazy sequences in a transaction may have the potential to cause problems. I can't offhand think of a scenario where it would, but it feels wrong. It could be argued that the realization of a lazy sequence is a side effect, and side effects shouldn't take place in a transaction, since transactions may run multiple times in the event of a conflict. Multiple realizations shouldn't cause a problem, but I can't say definitively that it's safe (honestly, I never use refs).
Make sure you actually need refs and transactions here. Transactions are for when you need to sequence multiple alterations to data, and need to be able to catch when the data involved has been changed part way through a transaction. If you just need a simple mutable container though, atom are much simpler.
Related
I'm developing a mini-social media API where the user is allowed to insert a new profile, connect two profiles together (like friends) and then receive recommendations based on the "friends of my friends" rule.
Right now I'm trying to create the API for Profile.
I have an atom that holds a list of maps, one for each profile.
(def profiles (atom ()))
(defn create [request]
(swap! profiles conj {:id (get-in request [:body "id"])
:name (get-in request [:body "name"])
:age (get-in request [:body "age"])
:recommendable (get-in request [:body "recommendable"])
:friends (list)
})
(created "")
)
I was trying to develop the find-by-id for the GET http verb for the API when I stumbled into a problem. How can I get the values from the maps within said list so I can apply functions to it?
For instance, here I was trying to use the filter function to return me only the maps that contained a given id. But I keep getting an error:
(defn find-by-id [id]
(filter #(= (:id %) id) profiles)
)
Dont know how to create ISeq from: clojure.lang.Atom
It seems to me that filter is not applicable to an Atom.
Same thing happens to remove:
(defn delete-by-id [id]
(swap! profiles (remove #(= (:id %) id) profiles))
)
When I try with #profiles I get an empty array as a result. And to make things worst when I tried the filter function using REPL it worked just fine.
Which leaves me wondering what am I missing here.
Could anyone please tell me what's going on?
Thanks in advance.
The first one fails because, as it says, atoms aren't a sequence, which filter is expecting.
You need to get the sequence out of the atom before you can filter it:
; I'm dereferencing the atom using # to get the list of profiles that it holds
(defn find-by-id [id]
(filter #(= (:id %) id) #profiles))
Note though, this isn't optimal. You're relying on the state of profiles that can change at seemingly random times (if you have asynchronous processes swap!ping it). It may complicate debugging since you can't get a good handle on the data before it's passed to filter. It also isn't good for the function to rely on profiles being an atom, since that's irrelevant to its function, and you may change your design later. It would be more future proof to make this function rely purely on its parameters and have no knowledge of the atom:
(defn find-by-id [id profiles]
(filter #(= (:id %) id) profiles))
; Then call it like this. I renamed your atom here
(find-by-id some-id #profile-atom)
Your second example fails because swap! accepts a function as its second argument. I think you meant to use reset!, which changes the value of the atom regardless of what it was before:
(defn delete-by-id [id]
(reset! profiles (remove #(= (:id %) id) #profiles)))
Although, this isn't optimal either. If you want to update an atom based on a previous state, use swap! instead and supply an updating function:
(defn delete-by-id [id]
(swap! profile-atom (fn [profiles] (remove #(= (:id %) id)) profiles)))
Or, slightly more succinctly:
(defn delete-by-id [id]
(swap! profile-atom (partial remove #(= (:id %) id))))
I'm partially applying remove to make a function. The old state of the atom is passed as the last argument to remove.
First, I have no experience with CS and Clojure is my first language, so pardon if the following problem has a solution, that is immediately apparent for a programmer.
The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. My approach revolves around a) storing temporarily the names of the atoms as strings in an atom itself; b) changing those strings to symbols with a function; c) using a function to add and create new atoms. The problem pertains to step "c": calling the function does not create new atoms, but using its body does create them.
All steps taken in the REPL are below (comments follow code blocks):
user=> (def atom-pool
#_=> (atom ["a1" "a2"]))
#'user/atom-pool
'atom-pool is the atom that stores intermediate to-be atoms as strings.
user=> (defn atom-symbols []
#_=> (mapv symbol (deref atom-pool)))
#'user/atom-symbols
user=> (defmacro populate-atoms []
#_=> (let [qs (vec (remove #(resolve %) (atom-symbols)))]
#_=> `(do ~#(for [s qs]
#_=> `(def ~s (atom #{}))))))
#'user/populate-atoms
'populate-atoms is the macro, that defines those atoms. Note, the purpose of (remove #(resolve %) (atom-symbols)) is to create only yet non-existing atoms. 'atom-symbols reads 'atom-pool and turns its content to symbols.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(nil nil nil)
Here it is confirmed that there are no 'a1', 'a2', 'a-new' atoms as of yet.
user=> (defn new-atom [a]
#_=> (do
#_=> (swap! atom-pool conj a)
#_=> (populate-atoms)))
#'user/new-atom
'new-atom is the function, that first adds new to-be atom as string to `atom-pool. Then 'populate-atoms creates all the atoms from 'atom-symbols function.
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Here we see that 'a1 'a2 were created as clojure.lang.Var$Unbound just by defining a function, why?
user=> (new-atom "a-new")
#'user/a2
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)
Calling (new-atom "a-new") did not create the 'a-new atom!
user=> (do
#_=> (swap! atom-pool conj "a-new")
#_=> (populate-atoms))
#'user/a-new
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 #'user/a-new)
user=>
Here we see that resorting explicitly to 'new-atom's body did create the 'a-new atom. 'a-new is a type of clojure.lang.Atom, but 'a1 and 'a2 were skipped due to already being present in the namespace as clojure.lang.Var$Unbound.
Appreciate any help how to make it work!
EDIT: Note, this is an example. In my project the 'atom-pool is actually a collection of maps (atom with maps). Those maps have keys {:name val}. If a new map is added, then I create a corresponding atom for this map by parsing its :name key.
"The summary of the question is as follows: one needs to create atoms at will with unknown yet symbols at unknown times. "
This sounds like a solution looking for a problem. I would generally suggest you try another way of achieving whatever the actual functionality is without generating vars at runtime, but if you must, you should use intern and leave out the macro stuff.
You cannot solve this with macros since macros are expanded at compile time, meaning that in
(defn new-atom [a]
(do
(swap! atom-pool conj a)
(populate-atoms)))
populate-atoms is expanded only once; when the (defn new-atom ...) form is compiled, but you're attempting to change its expansion when new-atom is called (which necessarily happens later).
#JoostDiepenmaat is right about why populate-atoms is not behaving as expected. You simply cannot do this using macros, and it is generally best to avoid generating vars at runtime. A better solution would be to define your atom-pool as a map of keywords to atoms:
(def atom-pool
(atom {:a1 (atom #{}) :a2 (atom #{})}))
Then you don't need atom-symbols or populate-atoms because you're not dealing with vars at compile-time, but typical data structures at run-time. Your new-atom function could look like this:
(defn new-atom [kw]
(swap! atom-pool assoc kw (atom #{})))
EDIT: If you don't want your new-atom function to override existing atoms which might contain actual data instead of just #{}, you can check first to see if the atom exists in the atom-pool:
(defn new-atom [kw]
(when-not (kw #atom-pool)
(swap! atom-pool assoc kw (atom #{}))))
I've already submitted one answer to this question, and I think that that answer is better, but here is a radically different approach based on eval:
(def atom-pool (atom ["a1" "a2"]))
(defn new-atom! [name]
(load-string (format "(def %s (atom #{}))" name)))
(defn populate-atoms! []
(doseq [x atom-pool]
(new-atom x)))
format builds up a string where %s is substituted with the name you're passing in. load-string reads the resulting string (def "name" (atom #{})) in as a data structure and evals it (this is equivalent to (eval (read-string "(def ...)
Of course, then we're stuck with the problem of only defining atoms that don't already exist. We could change the our new-atom! function to make it so that we only create an atom if it doesn't already exist:
(defn new-atom! [name]
(when-not (resolve (symbol name))
(load-string (format "(def %s (atom #{}))" name name))))
The Clojure community seems to be against using eval in most cases, as it is usually not needed (macros or functions will do what you want in 99% of cases*), and eval can be potentially unsafe, especially if user input is involved -- see Brian Carper's answer to this question.
*After attempting to solve this particular problem using macros, I came to the conclusion that it either cannot be done without relying on eval, or my macro-writing skills just aren't good enough to get the job done with a macro!
At any rate, I still think my other answer is a better solution here -- generally when you're getting way down into the nuts & bolts of writing macros or using eval, there is probably a simpler approach that doesn't involve metaprogramming.
Below is my attempt to iterate a sequence of maps; the code fails due to the casting error: Exception in thread "main" java.lang.RuntimeException: java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.util.Map$Entry.
Can anyone explain/demonstrate how I should iterate the result-set? Thanks.
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doall
(for [s [rs]]
(do (println (val s)))))))
You wrapped the rs into a vector. So s will be bound to the whole sequence, not the individual map entries. So when you call val it doesn't know what to do with a sequence. Hence the exception. This should work:
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doall
(for [rec rs
s rec]
(do
(println (val s)))))))
However the ugly doall and do around the for should ring a bell, that something could be improved. And indeed for is used to construct another lazy sequence. This does not work well with side-effects as you intend in your example. You should use doseq in this case.
(with-connection db
(with-query-results rs ["select category from users group by category"]
(doseq [rec rs
s rec]
(println (val s)))))
The interface for the bindings of doseq is identical to that of for. However it executes things immediatelly, and thusly realises any side-effects immediatelly. If you put multiple expressions in the body of a for, you have to wrap it into a do. This is a reminder that the body should produce a value. Multiple expressions however indicate side-effects. doseq therefore wraps the body into a do for you. So you can easily have multiple expressions. For illustration:
(doall
(for [s seq-of-maps]
(do
(println (key s))
(println (val s)))))
(doseq [s seq-of-maps]
(println (key s))
(println (val s)))))
As a rule of thumb: you need side-effects? Look for things starting in do!
As a rule of thumb 2: if something looks ugly (see above comparison), this should ring a bell.
OK, so it sounds like you are trying to do a DB query from Clojure. You may have to supply more information about the "users" table for instance and what your query result set looks like.
At any rate, something like this may work
(def a (with-query-results rs ["select category from users group by category"]
(doall rs)))
(map #(:category %) a)
This is a follow-up to my earlier question.
I came up with a bizarre object scheme from my reading of Let Over Lambda and can think of no advantages over protocols, but want to get opinions. I am just exploring the use of higher-order functions and encapsulation.
(defn new-person
"Construct a new Person object and return a Map of accessor methods."
[init-first-name init-last-name init-age]
(let [first-name (ref init-first-name)
last-name (ref init-last-name)
age (ref init-age)]
{:get-first-name #(#first-name)
:set-first-name #(dosync (ref-set first-name %1))
:get-last-name #(#last-name)
:set-last-name #(dosync (ref-set last-name %1))
:get-age #(#age)
:set-age #(dosync (ref-set age %1))}))
I can use the object like this:
(def fred (new-person "fred" "flintstone" 42))
and retrieve an accessor method this way:
(fred :get-age)
but I can't figure out how to call the accessor.
The object created is thread-safe since all mutation of "instance" variables occurs in the STM.
UPDATE: New and improved version:
(defn new-person
"Construct a new Person object and return a Map of accessor methods."
[init-first-name init-last-name init-age]
(let [first-name (ref init-first-name)
last-name (ref init-last-name)
age (ref init-age)]
{:first-name
(fn
([] #first-name)
([val] (dosync (ref-set first-name val))))
:last-name
(fn
([] #last-name)
([val] (dosync (ref-set last-name val))))
:age
(fn
([] #age)
([val] (dosync (ref-set age val))))}))
Maybe not a 100 % answer to your question, but would you try to do is not very idiomatic for Clojure. The 'standard' solution would be something like:
(defrecord Person [first-name last-name age])
(def fred (Person. "fred" "flintstone" 42))
(fred :age)
It looks like you are forcing OO mutable state into Clojure 'objects'
Its the same as your follow up question, wrap the form in another set of parenthesis. When an inner form returns a function, its the same as a symbol that returns a function. The rule is the first form in a parenthesis will always be looked up as a special form, macro, or function. You need something like quote to prevent this behavior.
user=> (fred :get-age)
#<user$new_person$fn__531 user$new_person$fn__531#c4afc4>
user=> ((fred :get-age))
42
Its the same as
user=> (let [age-getter (fred :get-age)] (age-getter))
42
Clojure philosophy is NOT to encapsulate access to the record fields themselves. Encapsulation should happen on higher levels, for example in the set of functions that work with that records. See Clojure - datatypes:
Encapsulation of information is folly fields are public, use protocols/interfaces to avoid dependencies
conj is used in (conj coll item), for example (conj #{} "Stu").
But, I found this example in 'Programming Clojure' book page16.
(alter visitors conj username)
I guess (conj visitors username) would be the correct usage. What's the secret?
The "secret" is that alter is a special function that is called in a transaction on a ref, and it has the effect of inserting the current value of the ref as the first parameter to whichever function is supplied as its second argument.
In fact the normal usage for conj with a ref would be:
(conj #visitors username)
So alter roughly translates as follows:
(alter vistors conj username)
=> (ref-set visitors (conj #visitors username))
It is not uncommon in Clojure for macros and higher order functions to take a function followed by args and then execute it by inserting a context-dependent argument before the supplied args. This makes caller's life a bit easier. This way you can write:
(alter visitors conj username)
instead of:
(alter visitors #(conj %1 username))
Some other forms that do this are: send,doto,update-in,->,alter-meta!.