Clojure if ( X and Y )? - clojure

The following code does not run as I am expecting. I want to be able to identify if foo and bar are empty and respond with an err accordingly else output "hello". However response only occurs when both foo and bar are present.
I'm new to Clojure and so probably missing something.
(defn create-entry [doc]
(let [id (str (java.util.UUID/randomUUID)) timestamp (quot (System/currentTimeMillis) 1000)]
(let [entry (assoc doc "id" id "timestamp" timestamp)]
(if (and (empty? [(get entry "foo") (empty? (get entry "bar")) ])
(response {:err "either foo or bar is required"})
) (prn "hello!")))))

You have some weird bracing issues going on. You stick both calls to get inside a vector, then check if that hard coded vector is empty.
I think you meant for your condition to be something more like:
(and (empty? (get entry "foo"))
(empty? (get entry "bar")))

I know this has been answered, but I thought I would add something here too. This could be better by splitting this up into two functions, one predicate, and call that from your create-entry function.
(defn entry? [entry]
(let [foo (get entry "foo")
bar (get entry "bar")]
(and (some? foo)
(some? bar))))
(defn create-entry [doc]
(let [id (str (UUID/randomUUID)) timestamp (quot (System/currentTimeMillis) 1000)]
(let [entry (assoc doc "id" id "timestamp" timestamp)]
(if (entry? entry)
(prn "hello!")
(response {:err "either foo or bar is required"})))))

Related

How do I add an element to an array-map in Clojure?

How can I add an element to an array-map in Clojure? I tried using assoc but it doesn't get added? I essentially want to set a default value of 0 for any missing items in the entry array-map.
(defn create-entry [doc]
(let [entry (assoc doc "id" (str (java.util.UUID/randomUUID)))]
(if (empty? (get entry "foo")) (assoc entry "foo" 0))
(if (empty? (get entry "bar")) (assoc entry "bar" 0))))
Update after comments from Carcigenicate:
(defn entry [doc]
(as-> (assoc doc "id" (str (java.util.UUID/randomUUID))) e
(if (empty? (get e "foo")) (assoc e "foo" 0) e)
(if (empty? (get e "bar")) (assoc e "bar" 0) e)))
(defn create-entry [doc]
(prn (entry doc)))
You need to starting thinking more functional. Note how all the structures you're using are immutable; they themselves can never change. Your second last line makes a copy of entry, but you never do anything with it; it's just thrown out. There are a few ways of dealing with situations like this where you need to transform a structure over a couple steps:
Just use let:
(let [entry (assoc doc "id" (str (java.util.UUID/randomUUID)))
def-foo (if (empty? (get entry "foo")) (assoc entry "foo" 0) entry)]
(if (empty? (get def-foo "bar")) (assoc def-foo "bar" 0) def-foo)))
Note how the last line uses the def-foo copy, instead of the original entry.
Use a threading macro:
; Create a new binding, e, that will hold the result of the previous form
(as-> (assoc doc "id" (str (java.util.UUID/randomUUID))) e
(if (empty? (get e "foo")) (assoc e "foo" 0) e)
(if (empty? (get e "bar")) (assoc e "bar" 0) e))
e is replaced by whatever the previous form evaluated to.
Note though, that if you ever find yourself using get and assoc on the same object, you might want to consider using update instead, which greatly simplifies everything, especially when paired with the -> threading macro:
(-> (assoc doc "id" (str (java.util.UUID/randomUUID)))
(update "foo" #(if (empty? %) 0 %))
(update "bar" #(if (empty? %) 0 %)))
I had to make some assumptions about what your intent was, because your code has an error that I didn't notice until after I had already submitted my answer. In your original code, your ifs don't evaluate to anything when the condition is false. I'm assuming you just don't want to change anything when they're false.
To supplement Carcigenicate's answer, another suggestion:
I'd use merge or assoc on a map of defaults:
(merge {:default-1 123 :default-2 234} {:default-1 "foo"})
=> {:default-1 "foo", :default-2 234}
Note that the order of arguments to merge matters i.e. right-most maps take precedence over left-most maps. Your default map values will only "survive" if they're not overridden by additional map(s).
(def defaults {"foo" 0, "bar" 0})
(defn create-entry [doc]
(assoc defaults "id" (str (java.util.UUID/randomUUID))))
(defn create-entry [doc]
(merge defaults {"id" (str (java.util.UUID/randomUUID))}))
Using assoc in this example has the same effect, and I'd prefer that version.

Improving complex data structure replacement

I'm attempting to modify a specific field in a data structure, described below (a filled example can be found here:
[{:fields "There are a few other fields here"
:incidents [{:fields "There are a few other fields here"
:updates [{:fields "There are a few other fields here"
:content "THIS is the field I want to replace"
:translations [{:based_on "Based on the VALUE of this"
:content "Replace with this value"}]}]}]}]
I already have this implemented it in a number of functions, as below:
(defn- translation-content
[arr]
(:content (nth arr (.indexOf (map :locale arr) (env/get-locale)))))
(defn- translate
[k coll fn & [k2]]
(let [k2 (if (nil? k2) k k2)
c ((keyword k2) coll)]
(assoc-in coll [(keyword k)] (fn c))))
(defn- format-update-translation
[update]
(dissoc update :translations))
(defn translate-update
[update]
(format-update-translation (translate :content update translation-content :translations)))
(defn translate-updates
[updates]
(vec (map translate-update updates)))
(defn translate-incident
[incident]
(translate :updates incident translate-updates))
(defn translate-incidents
[incidents]
(vec (map translate-incident incidents)))
(defn translate-service
[service]
(assoc-in service [:incidents] (translate-incidents (:incidents service))))
(defn translate-services
[services]
(vec (map translate-service services)))
Each array could have any number of entries (though the number is likely less than 10).
The basic premise is to replace the :content in each :update with the relevant :translation based on a provided value.
My Clojure knowledge is limited, so I'm curious if there is a more optimal way to achieve this?
EDIT
Solution so far:
(defn- translation-content
[arr]
(:content (nth arr (.indexOf (map :locale arr) (env/get-locale)))))
(defn- translate
[k coll fn & [k2]]
(let [k2 (if (nil? k2) k k2)
c ((keyword k2) coll)]
(assoc-in coll [(keyword k)] (fn c))))
(defn- format-update-translation
[update]
(dissoc update :translations))
(defn translate-update
[update]
(format-update-translation (translate :content update translation-content :translations)))
(defn translate-updates
[updates]
(mapv translate-update updates))
(defn translate-incident
[incident]
(translate :updates incident translate-updates))
(defn translate-incidents
[incidents]
(mapv translate-incident incidents))
(defn translate-service
[service]
(assoc-in service [:incidents] (translate-incidents (:incidents service))))
(defn translate-services
[services]
(mapv translate-service services))
I would start more or less as you do, bottom-up, by defining some functions that look like they will be useful: how to choose a translation from among a list of translations, and how to apply that choice to an update. But I wouldn't make the functions so tiny as yours: the logic is all spread out into a lot of places, and it's not easy to get an overall idea of what is going on. Here are the two functions I'd start with:
(letfn [(choose-translation [translations]
(let [applicable (filter #(= (:locale %) (get-locale))
translations)]
(when (= 1 (count applicable))
(:content (first applicable)))))
(translate-update [update]
(-> update
(assoc :content (or (choose-translation (:translations update))
(:content update)))
(dissoc :translations)))]
...)
Of course you can defn them instead if you'd like, and I suspect many people would, but they're only going to be used in one place, and they're intimately involved with the context in which they're used, so I like a letfn. These two functions are really all the interesting logic; the rest is just some boring tree-traversal code to apply this logic in the right places.
Now to build out the body of the letfn is straightforward, and easy to read if you make your code be the same shape as the data it manipulates. We want to walk through a series of nested lists, updating objects on the way, and so we just write a series of nested for comprehensions, calling update to descend into the right keyspace:
(for [user users]
(update user :incidents
(fn [incidents]
(for [incident incidents]
(update incident :updates
(fn [updates]
(for [update updates]
(translate-update update))))))))
I think using for here is miles better than using map, although of course they are equivalent as always. The important difference is that as you read through the code you see the new context first ("okay, now we're doing something to each user"), and then what is happening inside that context; with map you see them in the other order and it is difficult to keep tack of what is happening where.
Combining these, and putting them into a defn, we get a function that you can call with your example input and which produces your desired output (assuming a suitable definition of get-locale):
(defn translate [users]
(letfn [(choose-translation [translations]
(let [applicable (filter #(= (:locale %) (get-locale))
translations)]
(when (= 1 (count applicable))
(:content (first applicable)))))
(translate-update [update]
(-> update
(assoc :content (or (choose-translation (:translations update))
(:content update)))
(dissoc :translations)))]
(for [user users]
(update user :incidents
(fn [incidents]
(for [incident incidents]
(update incident :updates
(fn [updates]
(for [update updates]
(translate-update update))))))))))
we can try to find some patterns in this task (based on the contents of the snippet from github gist, you've posted):
simply speaking, you need to
1) update every item (A) in vector of data
2) updating every item (B) in vector of A's :incidents
3) updating every item (C) in vector of B's :updates
4) translating C
The translate function could look like this:
(defn translate [{translations :translations :as item} locale]
(assoc item :content
(or (some #(when (= (:locale %) locale) (:content %)) translations)
:no-translation-found)))
it's usage (some fields are omitted for brevity):
user> (translate {:id 1
:content "abc"
:severity "101"
:translations [{:locale "fr_FR"
:content "abc"}
{:locale "ru_RU"
:content "абв"}]}
"ru_RU")
;;=> {:id 1,
;; :content "абв",
;; :severity "101",
;; :translations [{:locale "fr_FR", :content "abc"} {:locale "ru_RU", :content "абв"}]}
then we can see that 1 and 2 are totally similar, so we can generalize that:
(defn update-vec-of-maps [data k f]
(mapv (fn [item] (update item k f)) data))
using it as a building block you can make up the whole data transformation:
(defn transform [data locale]
(update-vec-of-maps
data :incidents
(fn [incidents]
(update-vec-of-maps
incidents :updates
(fn [updates] (mapv #(translate % locale) updates))))))
(transform data "it_IT")
returns what you need.
then you can generalize it further, making the utility function for arbitrary depth transformations:
(defn deep-update-vec-of-maps [data ks terminal-fn]
(if (seq ks)
((reduce (fn [f k] #(update-vec-of-maps % k f))
terminal-fn (reverse ks))
data)
data))
and use it like this:
(deep-update-vec-of-maps data [:incidents :updates]
(fn [updates]
(mapv #(translate % "it_IT") updates)))
I recommend you look at https://github.com/nathanmarz/specter
It makes it really easy to read and update clojure data structures. Same performance as hand-written code, but much shorter.

how to avoid nesting in clojure

when my write a function to check a user can delete a post by clojure,I get this
(defn delete!
{:arglists}
[^String id]
(if (valid-number? id)
(let [result {:code 200 :status "error" :messag "delete success"}]
(if-let [user (session/get :userid)]
(if-let [post (pdb/id id)]
(if (= user (post :user_id))
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok"))
(assoc result :message (emsg :not-own)))
(assoc result :message (emsg :post-id-error))))
(assoc result :message (emsg :not-login)))))
so i want to fix it,i get this
https://github.com/4clojure/4clojure/blob/develop/src/foreclojure/register.clj#L27
https://github.com/4clojure/4clojure/blob/develop/src/foreclojure/utils.clj#L32
but it is line,but not a nest.
the delete! function is nest ugly and it is very hard to understand it,how to write a macro to avoid the nesting a lot.or other way to avoid it.
This doesn't need a macro. I guess cond is a macro, but it is the only one we need to make this code readable.
(defn delete!
;; {:arglists} ; this line will not compile
[^String id]
(let [result {:code 200 :status "error" :message "delete success"}
user (session/get :userid)
post (and user (valid-number? id) (pbd/id id))]
(cond
(not user)
(assoc result :message (emsg :not-login))
(not post)
(assoc result :message (emsg :post-id-error))
(not= user (:user_id post))
(assoc result :message (emsg :not-own))
:else
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok")))))
This is something a lot of people run into, so don't feel bad.
Check out this blog by Christophe Grand, which I think is a pretty nice (and concise!) solution.
Edit: you only need something fancy like this (or alternatively the version using delay in this other post) if you need to short-circuit execution like the original - otherwise noisesmith's answer is the way to go.
Here's how you could do this sort of thing with the Either monad -- I'm sure there are libraries for it already but I'll implement it here for completeness. (Note: this code hasn't been validated.)
(defn success? [v]
(contains? v :success))
(defn inject [v]
{:success v})
(defn bind [v f]
(if (success? v)
(apply f (:success v))
v))
(defmacro >>= [v & body]
(let [binds (map #(list 'bind %) body)]
`(-> ~v ~#binds)))
(defn delete!
{:arglists}
[^String id]
(if (valid-number? id)
(let [result {:code 200 :status "error" :message "delete success"}
check
(>>= (inject {:id id})
#(if-let [user (session/get :userid)]
{:success (assoc % :user user)}
(:failure (assoc result :message (emsg :not-login))))
#(if-let [post (pdb/id (:id %))]
{:success (assoc % :post post)}
{:failure (assoc result :message (emsg :post-id-error))})
#(if (= (:user %) ((:post %) :user_id))
{:success %}
{:failure (assoc result :message (emsg :not-own))}))]
(if (success? check)
(do
(pdb/delete! (Long/valueOf id))
(assoc result :status "ok"))
(:failure check)))))
The >>= macro works like the -> macro (obviously, since it uses it), but if any of the functions return a {:failure ...} then the chain short-circuits (thanks to bind) and the failure value of the function that failed becomes the value returned by >>=.
Edit
I should note that the function I have named inject is actually called return, but I decided to name it inject here since that's more along the lines of what it does in this monad.

how to separate concerns in the below fn

The function below does 2 things -
Checks if the atom is nil or fetch-agin is true, and then fetches the data.
It processes the data by calling (add-date-strings).
What is a better pattern to separate out the above two concerns ?
(def retrieved-data (atom nil))
(defn fetch-it!
[fetch-again?]
(if (or fetch-again?
(nil? #retrieved-data))
(->> (exec-services)
(map #(add-date-strings (:time %)))
(reset! retrieved-data))
#retrieved-data))
One possible refactoring would be:
(def retrieved-data (atom nil))
(defn fetch []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-it!
([]
(fetch-it! false))
([force]
(if (or force (nil? #retrieved-data))
(reset! retrieved-data (fetch))
#retrieved-data)))
By the way, the pattern to seperate out concerns is called "functions" :)
To really separate the concerns I think it might be better to define a separate fetch and process function. So that in no way they are complected.
(def retrieved-data (atom nil))
(defn fetcher []
(->> (exec-services)
(map #(add-date-strings (:time %)))))
(defn fetch-again? [force]
(fn [data] (or force (nil? data))))
(defn fetch-it! [fetch-fn data fetch-again?]
(when (fetch-again? #data))
(reset! data (fetch-fn))))
;;Usage
(fetch-it! fetcher retrieved-data (fetch-again? true))
Notice that I also gave the data atom as an argument.

How to do hooks in Clojure

I have a situation where I am creating and destroying objects in one clojure namespace, and want another namespace to co-ordinate. However I do not want the first namespace to have to call the second explicitly on object destruction.
In Java, I could use a listener. Unfortunately the underlying java libraries do not signal events on object destruction. If I were in Emacs-Lisp, then I'd use hooks which do the trick.
Now, in clojure I am not so sure. I have found the Robert Hooke library https://github.com/technomancy/robert-hooke. But this is more like defadvice in elisp terms -- I am composing functions. More over the documentation says:
"Hooks are meant to extend functions you don't control; if you own the target function there are obviously better ways to change its behaviour."
Sadly, I am not finding it so obvious.
Another possibility would be to use add-watch, but this is marked as alpha.
Am I missing another obvious solution?
Example Added:
So First namespace....
(ns scratch-clj.first
(:require [scratch-clj.another]))
(def listf (ref ()))
(defn add-object []
(dosync
(ref-set listf (conj
#listf (Object.))))
(println listf))
(defn remove-object []
(scratch-clj.another/do-something-useful (first #listf))
(dosync
(ref-set listf (rest #listf)))
(println listf))
(add-object)
(remove-object)
Second namespace
(ns scratch-clj.another)
(defn do-something-useful [object]
(println "object removed is:" object))
The problem here is that scratch-clj.first has to require another and explicitly push removal events across. This is a bit clunky, but also doesn't work if I had "yet-another" namespace, which also wanted to listen.
Hence I thought of hooking the first function.
Is this solution suitable to your requirements?
scratch-clj.first:
(ns scratch-clj.first)
(def listf (atom []))
(def destroy-listeners (atom []))
(def add-listeners (atom []))
(defn add-destroy-listener [f]
(swap! destroy-listeners conj f))
(defn add-add-listener [f]
(swap! add-listeners conj f))
(defn add-object []
(let [o (Object.)]
(doseq [f #add-listeners] (f o))
(swap! listf conj o)
(println #listf)))
(defn remove-object []
(doseq [f #destroy-listeners] (f (first #listf)))
(swap! listf rest)
(println #listf))
Some listeners:
(ns scratch-clj.another
(:require [scratch-clj.first :as fst]))
(defn do-something-useful-on-remove [object]
(println "object removed is:" object))
(defn do-something-useful-on-add [object]
(println "object added is:" object))
Init binds:
(ns scratch-clj.testit
(require [scratch-clj.another :as another]
[scratch-clj.first :as fst]))
(defn add-listeners []
(fst/add-destroy-listener another/do-something-useful-on-remove)
(fst/add-add-listener another/do-something-useful-on-add))
(defn test-it []
(add-listeners)
(fst/add-object)
(fst/remove-object))
test:
(test-it)
=> object added is: #<Object java.lang.Object#c7aaef>
[#<Object java.lang.Object#c7aaef>]
object removed is: #<Object java.lang.Object#c7aaef>
()
It sounds a lot like what you're describing is callbacks.
Something like:
(defn make-object
[destructor-fn]
{:destructor destructor-fn :other-data "data"})
(defn destroy-object
[obj]
((:destructor obj) obj))
; somewhere at the calling code...
user> (defn my-callback [o] (pr [:destroying o]))
#'user/my-callback
user> (destroy-object (make-object my-callback))
[:destroying {:destructor #<user$my_callback user$my_callback#73b8cdd5>, :other-data "data"}]
nil
user>
So, here is my final solution following mobytes suggestion. A bit more work, but
I suspect that I will want this in future.
Thanks for all the help
;; hook system
(defn make-hook []
(atom []))
(defn add-hook [hook func]
(do
(when-not
(some #{func} #hook)
(swap! hook conj func))
#hook))
(defn remove-hook [hook func]
(swap! hook
(partial
remove #{func})))
(defn clear-hook [hook]
(reset! hook []))
(defn run-hook
([hook]
(doseq [func #hook] (func)))
([hook & rest]
(doseq [func #hook] (apply func rest))))
(defn phils-hook []
(println "Phils hook"))
(defn phils-hook2 []
(println "Phils hook2"))
(def test-hook (make-hook))
(add-hook test-hook phils-hook)
(add-hook test-hook phils-hook2)
(run-hook test-hook)
(remove-hook test-hook phils-hook)
(run-hook test-hook)