Im recently using reagent and re-frame for my clojurescript project and i have a problem:
So i have html custom tags
<question id="1"></question>
<question id="2"></question>
And i want to swap them into my reagent-generated html using cljs for function
(defn mypanel []
[:p "Hi!"])
(let [q (.getElementsByTagName js/document "question")]
(for [i (range 2)]
^{:keys i}
(reagent/render [mypanel]
(aget (.getElementsByTagName js/document "question") i))))
But it doesn't work, i tried to test it without using the for function by
(reagent/render [mypanel]
(aget (.getElementsByTagName js/document "question") 0))
and it worked just fine with only one tag.
And i don't know why the for function doesn't work, or does reagent doesn't work that way? Anybody have a suggestion?
I'm very noob at this.
for produces a lazy sequence, which means that none of the work to evaluate the sequence is done until it is required. You can't use lazy sequences to force side effects, as they will never be evaluated (render is one such place). To force side effects you should probably replace it with doseq. And in your case dotimes would probably be better:
(let [q (.getElementsByTagName js/document "question")]
(dotimes [i 2]
^{:keys i}
(reagent/render [mypanel]
(aget (.getElementsByTagName js/document "question") i))))
The other option could be to force the lazyseq returned by for:
(doall (for [i (range 2)]....
Related
Consider a dataset like this:
(def data [{:url "http://www.url1.com" :type :a}
{:url "http://www.url2.com" :type :a}
{:url "http://www.url3.com" :type :a}
{:url "http://www.url4.com" :type :b}])
The contents of those URL's should be requested in parallel. Depending on the item's :type value those contents should be parsed by corresponding functions. The parsing functions return collections, which should be concatenated, once all the responses have arrived.
So let's assume that there are functions parse-a and parse-b, which both return a collection of strings when they are passed a string containing HTML content.
It looks like core.async could be a good tool for this. One could either have separate channels for each item ore one single channel. I'm not sure which way would be preferable here. With several channels one could use transducers for the postprocessing/parsing. There is also a special promise-chan which might be proper here.
Here is a code-sketch, I'm using a callback based HTTP kit function. Unfortunately, I could not find a generic solution inside the go block.
(defn f [data]
(let [chans (map (fn [{:keys [url type]}]
(let [c (promise-chan (map ({:a parse-a :b parse-b} type)))]
(http/get url {} #(put! c %))
c))
data)
result-c (promise-chan)]
(go (put! result-c (concat (<! (nth chans 0))
(<! (nth chans 1))
(<! (nth chans 2))
(<! (nth chans 3)))))
result-c))
The result can be read like so:
(go (prn (<! (f data))))
I'd say that promise-chan does more harm than good here. The problem is that most of core.async API (a/merge, a/reduce etc.) relies on fact that channels will close at some point, promise-chans in turn never close.
So, if sticking with core.async is crucial for you, the better solution will be not to use promise-chan, but ordinary channel instead, which will be closed after first put!:
...
(let [c (chan 1 (map ({:a parse-a :b parse-b} type)))]
(http/get url {} #(do (put! c %) (close! c)))
c)
...
At this point, you're working with closed channels and things become a bit simpler. To collect all values you could do something like this:
;; (go (put! result-c (concat (<! (nth chans 0))
;; (<! (nth chans 1))
;; (<! (nth chans 2))
;; (<! (nth chans 3)))))
;; instead of above, now you can do this:
(->> chans
async/merge
(async/reduce into []))
UPD (below are my personal opinions):
Seems, that using core.async channels as promises (either in form of promise-chan or channel that closes after single put!) is not the best approach. When things grow, it turns out that core.async API overall is (you may have noticed that) not that pleasant as it could be. Also there are several unsupported constructs, that may force you to write less idiomatic code than it could be. In addition, there is no built-in error handling (if error occurs within go-block, go-block will silently return nil) and to address this you'll need to come up with something of your own (reinvent the wheel). Therefore, if you need promises, I'd recommend to use specific library for that, for example manifold or promesa.
I wanted this functionality as well because I really like core.async but I also wanted to use it in certain places like traditional JavaScript promises. I came up with a solution using macros. In the code below, <? is the same thing as <! but it throws if there's an error. It behaves like Promise.all() in that it returns a vector of all the returned values from the channels if they all are successful; otherwise it will return the first error (since <? will cause it to throw that value).
(defmacro <<? [chans]
`(let [res# (atom [])]
(doseq [c# ~chans]
(swap! res# conj (serverless.core.async/<? c#)))
#res#))
If you'd like to see the full context of the function it's located on GitHub. It's heavily inspired from David Nolen's blog post.
Use pipeline-async in async.core to launch asynchronous operations like http/get concurrently while delivering the result in the same order as the input:
(let [result (chan)]
(pipeline-async
20 result
(fn [{:keys [url type]} ch]
(let [parse ({:a parse-a :b parse-b} type)
callback #(put! ch (parse %)(partial close! ch))]
(http/get url {} callback)))
(to-chan data))
result)
if anyone is still looking at this, adding on to the answer by #OlegTheCat:
You can use a separate channel for errors.
(:require [cljs.core.async :as async]
[cljs-http.client :as http])
(:require-macros [cljs.core.async.macros :refer [go]])
(go (as-> [(http/post <url1> <params1>)
(http/post <url2> <params2>)
...]
chans
(async/merge chans (count chans))
(async/reduce conj [] chans)
(async/<! chans)
(<callback> chans)))
I'm trying to handle following DSL:
(simple-query
(is :category "car/audi/80")
(is :price 15000))
that went quite smooth, so I added one more thing - options passed to the query:
(simple-query {:page 1 :limit 100}
(is :category "car/audi/80")
(is :price 15000))
and now I have a problem how to handle this case in most civilized way. as you can see simple-query may get hash-map as a first element (followed by long list of criteria) or may have no hash-mapped options at all. moreover, I would like to have defaults as a default set of options in case when some (or all) of them are not provided explicite in query.
this is what I figured out:
(def ^{:dynamic true} *defaults* {:page 1
:limit 50})
(defn simple-query [& body]
(let [opts (first body)
[params criteria] (if (map? opts)
[(merge *defaults* opts) (rest body)]
[*defaults* body])]
(execute-query params criteria)))
I feel it's kind of messy. any idea how to simplify this construction?
To solve this problem in my own code, I have a handy function I'd like you to meet... take-when.
user> (defn take-when [pred [x & more :as fail]]
(if (pred x) [x more] [nil fail]))
#'user/take-when
user> (take-when map? [{:foo :bar} 1 2 3])
[{:foo :bar} (1 2 3)]
user> (take-when map? [1 2 3])
[nil [1 2 3]]
So we can use this to implement a parser for your optional map first argument...
user> (defn maybe-first-map [& args]
(let [defaults {:foo :bar}
[maybe-map args] (take-when map? args)
options (merge defaults maybe-map)]
... ;; do work
))
So as far as I'm concerned, your proposed solution is more or less spot on, I would just clean it up by factoring out parser for grabbing the options map (here into my take-when helper) and by factoring out the merging of defaults into its own binding statement.
As a general matter, using a dynamic var for storing configurations is an antipattern due to potential missbehavior when evaluated lazily.
What about something like this?
(defn simple-query
[& body]
(if (map? (first body))
(execute-query (merge *defaults* (first body)) (rest body))
(execute-query *defaults* body)))
Clojure beginner here! I added flash message support to my Hiccup code in a Noir project.
What I'm trying to do is check if the message string for each specific was set or not. If there's no message, then I don't want to display the specific flash element containing that message.
(defpartial success-flash [msg]
[:div.alert.notice.alert-success
[:a.close {:data-dismiss "alert"} "x"]
[:div#flash_notice msg]])
(defpartial error-flash [msg]
[:div.alert.notice.alert-error
[:a.close {:data-dismiss "alert"} "x"]
[:div#flash_notice msg]])
[..]
(defpartial layout [& content]
(html5
[:head
[...]
[:body
(list
[...]
[:div.container
(let [error-msg (session/flash-get :error-message)
error-div (if (nil? error-msg) () (error-flash error-msg))
success-msg (session/flash-get :success-message)
success-div (if (nil? success-msg) () (success-flash success-msg))]
warning-msg (session/flash-get :warning-message)
warning-div (if (nil? warning-msg) () (warning-flash warning-msg))]
(list error-div success-div warning-div content))])]))
Disclaimer: I completely agree that you won't likely ever be in a situation where you'll need more than one of those specific flashes on at once, but indulge me in my attempt at figuring out a better and more functional way of implementing this.
I'm confident that there's a pattern out there for handling similar situations. Basically I check the value of several expressions, do a bunch of stuff with those values, and then act based on the results. You could pull this off with a progressively more and more monstrous (cond), but my solution is at least somewhat cleaner.
Tips?
You could also use when-let.
(defpartial layout
[& contents]
(html5
[:body
(when-let [msg (session/flash-get :error-message)]
(error-flash msg))
(when-let [msg (session/flash-get :warning-message)]
(warning-flash msg))
(when-let [msg (session/flash-get :success-message)]
(success-flash msg))
contents))
I'm not a hiccup expert, but I think this should work. I find it a little clearer on what's going on, although it's slightly more verbose.
The pattern is called mapping value. Below is an example that uses keep function to apply the pattern of mapping values and then filtering them
(use 'clojure.contrib.core)
(def flash-message
[[:error-message error-flash]
[:success-message success-flash]
[:warning-message warning-flash]])
(keep (fn [m f] (-?>> m (session/flash-get) (f))) flash-message)
How to make clojure to count '() as nil?
For example:
How to make something like
(if '() :true :false)
;to be
:false
;Or easier
(my-fun/macro/namespace/... (if '() :true :false))
:false
And not just if. In every way.
(= nil '()) or (my-something (= nil '()))
true
And every code to be (= '() nil) save.
(something (+ 1 (if (= nil '()) 1 2)))
2
I was thinking about some kind of regural expression. Which will look on code and replace '() by nil, but there are some things like (rest '(1)) and many others which are '() and I am not sure how to handle it.
I was told that macros allow you to build your own languages. I want to try it by changing clojure. So this is much about "How clojure works and how to change it?" than "I really need it to for my work."
Thank you for help.
'() just isn't the same thing as nil - why would you want it do be?
What you might be looking for though is the seq function, which returns nil if given an empty collection:
(seq [1 2 3])
=> (1 2 3)
(seq [])
=> nil
(seq '())
=> nil
seq is therefore often used to test for "emptiness", with idioms like:
(if (seq coll)
(do-something-with coll)
(get-empty-result))
You say you would like to change Clojure using the macros. Presently, as far as I know, this is not something you could do with the "regular" macro system (terminology fix anyone?). What you would really need (I think) is a reader macro. Things I have seen online (here, for example) seem to say that there exists something like reader macros in Clojure 1.4--but I have no familiarity with this because I really like using clooj as my IDE, and it currently is not using Clojure 1.4. Maybe somebody else has better info on this "extensible reader" magic.
Regardless, I don't really like the idea of changing the language in that way, and I think there is a potentially very good alternative: namely, the Clojure function not-empty.
This function takes any collection and either returns that collection as is, or returns nil if that collection is empty. This means that anywhere you will want () to return nil, you should wrap it not-empty. This answer is very similar to mikera's answer above, except that you don't have to convert your collections to sequences (which can be nice).
Both using seq and not-empty are pretty silly in cases where you have a "hand-written" collection. After all, if you are writing it by hand (or rather, typing it manually), then you are going to know for sure whether or not it is empty. The cases in which this is useful is when you have an expression or a symbol that returns a collection, and you do not know whether the returned collection will be empty or not.
Example:
=> (if-let [c (not-empty (take (rand-int 5) [:a :b :c :d]))]
(println c)
(println "Twas empty"))
;//80% of the time, this will print some non-empty sub-list of [:a :b :c :d]
;//The other 20% of the time, this will return...
Twas empty
=> nil
What about empty? ? It's the most expressive.
(if (empty? '())
:true
:false)
You can override macros and functions. For instance:
(defn classic-lisp [arg]
(if (seq? arg) (seq arg) arg))
(defn = [& args]
(apply clojure.core/= (map classic-lisp args)))
(defmacro when [cond & args]
`(when (classic-lisp ~cond) ~#args))
Unfortunately, you can't override if, as it is a special form and not a macro. You will have to wrap your code with another macro.
Let's make an if* macro to be an if with common-lisp behavior:
(defmacro if* [cond & args]
`(if (classic-lisp ~cond) ~#args)
With this, we can replace all ifs with if*s:
(use 'clojure.walk)
(defn replace-ifs [code]
(postwalk-replace '{if if*} (macroexpand-all code)))
(defmacro clojure-the-old-way [& body]
`(do ~#(map replace-ifs body)))
Now:
=> (clojure-the-old-way (if '() :true :false) )
:false
You should be able to load files and replace ifs in them too:
(defn read-clj-file [filename]
;; loads list of clojure expressions from file *filename*
(read-string (str "(" (slurp filename) ")")))
(defn load-clj-file-the-old-way [filename]
(doseq [line (replace-ifs (read-clj-file filename))] (eval line))
Note that I didn't test the code to load files and it might be incompatible with leiningen or namespaces. I believe it should work with overriden = though.
Here is the sample code I want to get to work:
(letfn [(CONC [f] f)
(CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
Is there some voodo that will make it call the CONC function with hamster as the parameter? That is, is there some way to convert the string "CONC" into a function that is not bound to a namespace but rather to a local binding?
EDIT:
To be clearer, the way this will be called is:
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
I'd probably solve this by creating a map of functions indexed by strings:
(def voodoo
{"CONC" (fn [f] f)
"CONT" (fn [f] (str "\newline" f))})
Then your desired code should work directly (exploiting the fact that a map is a function that looks up it's argument)
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
Note that the functions here are fully anonymous - you don't need them to be referenced anywhere in the namespace for this to work. In my view this is a good thing, because unless you also need the functions somewhere else then it's best to avoid polluting your top-level namespace too much.
No. Eval does not have access to the local/lexical environment, ever.
Edit: This is not a very good answer, and not really accurate either. You could write voodoo as a macro, and then it doesn't need runtime access to the lexical environment, just compile-time. However, this means it would only work if you know at compile time that the function you want to call is x, and so it wouldn't be very useful - why not just type x instead of (voodoo "x")?
(defmacro voodoo [fname]
(symbol fname))
(letfn [(x [y] (inc y))]
((voodoo "x") 2))
;; 3
(letfn [(x [y] (inc y))]
(let [f "x"]
((voodoo f) 2)))
;; error
Well, it's sort of possible:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
...and now we can do weird stuff like this:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
That "Evaluation aborted" means an exception was thrown.
You could also replace the throw branch of the if in voodoo with (resolve (symbol ~s)) to defer to the globals if no local is found:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
...and now this works with definition of example as above (though note that if you are experimenting at the REPL, you will need to recompile example after redefining voodoo):
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
Now, this is an abuse of Clojure's facilities which one would do well to try to do without. If one cannot, one should probably turn to evalive by Michael Fogus; it's a library which provides an "eval-with-locals" facility in the form of an evil function and a couple of utilities. The functionality seems to be well factored too, e.g. something like the ~(zipmap ...) thing above is encapsulated as a macro and evil there appears to be almost a drop-in replacement for eval (add the env parameter and you're good to go). I haven't read the source properly, but I probably will now, looks like fun. :-)
Im not really clear what you are asking for so i'll try a couple answers:
if you have a string that is the name of the function you wish to call:
(def name "+")
((find-var (symbol (str *ns* "/" name))) 1 2 3)
this would give voodoo a deffinition like this:
(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
clojure.core=>
this assumes your function is in the current namepace ns.
if you want to turn a string into a function you could use this pattern
(let [f (eval (read-string "(fn [] 4)"))] (f))