Why does let-defined atom provide a different result? - clojurescript

I try to deasync some js-calls using clojure script. If I let the atom in the deasync function itself there is a suprising result:
(let [result (atom nil)
timeout (atom 10)]
(go
(let [async-result (<p! (js/Promise.resolve 42))]
(swap! result (constantly async-result))
(js/console.log #result)))
(while (and (nil? #result)
(pos? #timeout))
(do (debug #result)
(debug #timeout)
(swap! timeout dec)))
#result)
--- output is ---
null
10
...
null
1
42
nil
If I define the atom outside the result is as intended:
(def result (atom nil))
(let [timeout (atom 10)]
(go
(let [async-result (<p! (js/Promise.resolve 42))]
(swap! result (constantly async-result))
(js/console.log #result)))
(while (and (nil? #result)
(pos? #timeout))
(do (debug #result)
(debug #timeout)
(swap! timeout dec)))
#result)
--- output is ---
42
42
Can someone declare the difference?
I think using a namespace global atom is not quite a good idea for a deasync function ...

It is not possible to write a "deasync" function, if by that you mean writing a function that "blocks" to wait for an async result. JavaScript is single-threaded so if you block in a loop to wait for an async result that loop will prevent the async work from ever happening.
The result in your program above will always be that it loops 10 times while the result is nil and then eventually yield control to let the queued microtask execute the go.
I suspect that you just maybe tried to run this from the REPL and did not redefine the result atom after one test run. So in the first pass it was nil but then got set to 42. On the second run it then starts with 42 and your loop never loops. You can easily verify this by "labeling" your results. So instead of (js/console.log #result) you use (js/console.log "go-result" #result). You'll see that you'll always get the loop result first and then the go-result.
BTW if you just want to set a specific atom value just use reset! instead of swap! with constantly, eg. (reset! result async-result).

Related

ClojureScript. Reset atom in Reagent when re-render occurs

I'm displaying a set of questions for a quiz test and I'm assigning a number to each question just to number them when they are shown in the browser:
(defn questions-list
[]
(let [counter (atom 0)]
(fn []
(into [:section]
(for [question #(re-frame/subscribe [:questions])]
[display-question (assoc question :counter (swap! counter inc))])))))
The problem is that when someone edits a question in the browser (and the dispatch is called and the "app-db" map is updated) the component is re-rendered but the atom "counter" logically starts from the last number not from zero. So I need to reset the atom but I don't know where. I tried with a let inside the anonymous function but that didn't work.
In this case I'd just remove the state entirely. I haven't tested this code, but your thinking imperatively here. The functional version of what your trying to do is something along the lines of:
Poor but stateless:
(let [numbers (range 0 (count questions))
indexed (map #(assoc (nth questions %) :index %) questions)]
[:section
(for [question indexed]
[display-question question])])
but this is ugly, and nth is inefficient. So lets try one better. Turns out map can take more than one collection as it's argument.
(let [numbers (range 0 (count questions))
indexed (map (fn [idx question] (assoc question :index idx)) questions)]
[:section
(for [question indexed]
[display-question question])])
But even better, turns out there is a built in function for exactly this. What I'd actually write:
[:section
(doall
(map-indexed
(fn [idx question]
[display-question (assoc question :index idx)])
questions))]
Note: None of this code has actually been run, so you might have to tweak it a bit before it works. I'd recommend looking up all of the functions in ClojureDocs to make sure you understand what they do.
If you need counter to be just an index for a question, you could instead use something like this:
(defn questions-list
[]
(let [questions #(re-frame/subscribe [:questions])
n (count questions)]
(fn []
[:section
[:ul
(map-indexed (fn [idx question] ^{:key idx} [:li question]) questions)]])))
Note: here I used [:li question] because I assumed that question is some kind of text.
Also, you could avoid computing the count for questions in this component and do it with a layer 3 subscription:
(ns your-app.subs
(:require
[re-frame.core :as rf]))
;; other subscriptions...
(rf/reg-sub
:questions-count
(fn [_ _]
[(rf/subscribe [:questions])])
(fn [[questions] _]
(count questions)))
Then in the let binding of your component you would need to replace n (count questions) with n #(re-frame/subscribe [:questions-count]).

Apply transducer to stream of event callbacks

I'm writing some clojurescript which is doing some logs processing. I'm wrapping a Javascript library that gives me a callback whenever a new log entry arrives, i.e.
(.on my-logs-source "log-entry" handle-log-event)
I'd like to perform some simple aggregation on these events using something like clojure's partition-by function, which returns a transducer, and get a vector of the results. What is the idiomatic way to transform my stream of event callbacks into something that I can apply a transducer to?
You can use clojure.async - its channels optionally accept transducers when constructed.
The example below illustrates how you can achieve your goal.
(require '[clojure.core.async :as async])
(def ch (async/chan 1 (partition-by odd?)))
(def callback (fn [n] (async/put! ch n)))
(async/go-loop []
(when-some [v (async/<! ch)]
(println "Got" v)
(recur)))
(callback 1)
(callback 1)
(callback 2)
(callback 2)
(callback 3)
(callback 4)
The code above will create a channel with a transducer. Your callback function will send all values received to that channel. The go block will consume values from the channel as they become available. The values consumed from the channel are the results produced by the transducer.
For the example REPL session above the console output from your go block will be following:
Got [1 1]
Got [2 2]
Got [3]
Now when you close the channel:
(async/close! ch)
the remaining data will be "flushed" from your transducer:
Got [4]

No more than 1024 pending put on a single channel

I'm not too familiar with core.async, but from what I've read, I know I can do stuff like this:
;; fetch data
(defn get-data-from-server
[]
(let [ch (chan)]
(fetch-data-from-server (fn [result]
(put! ch result)))
ch))
;; echo data
(go-loop []
(let [v (<! (get-data-from-server))]
(.log js/console v)))
Assume I need to get fresh data from server for every 1000ms, this is what I did:
(defn get-data-from-server
[]
(let [ch (chan)]
(.setInterval js/window
(fetch-data-from-server (fn [result]
(put! ch result))) 1000)
ch))
After some time it complains something like No more than 1024 pending put on a single channel, consider using ..... Any suggestion?
Why are you creating a brand new channel every time your go-loop runs? Perhaps you should not create the channel inside the go loop and only reference an already-created channel. That tends to be much more common and better performant.

Is there a way to make a function var dynamic after definition?

Post updated to make it relevant to the course of events (responses and eliminating clutter).
Thank you very much for your time and help !
In some previous version of Clojure every var could be bound with a "binding" form.
Nowadays you get "Can't dynamically bind non-dynamic var" if not defined as dynamic.
In some contexts making a function var dynamic after definition could be useful (stubbing/mocking in testing).
Don't try:
(def ^{:dynamic true} log-call #'log-call)
It will eventually cause StackOverflowError since you are defining a function that calls itself (thank you for your explanation).
The updated question:
The approach suggested does not seem to work.
Forms called from the binding form don't get the binding defined.
Could you please help to figure out what I'm missing??
Here is the updated code:
(def all-expenses [{:amount 33.0 :date "today"}
{:amount 44.0 :date "yesterday"}])
(defn fetch-all-expenses [])
(defn fetch-expenses-greater-than [threshold]
(let [all (fetch-all-expenses)]
;calling from a nested form does not see the dynamically bound definition!
(println "2) from fetch-expenses-greater-than:" (fetch-all-expenses))
all))
(defn wrap [f]
(fn [& args] (apply f args)))
(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))
(binding [fetch-all-expenses (constantly all-expenses)]
(let [filtered (fetch-expenses-greater-than 15.0)]
(println "1) from inside binding:" (fetch-all-expenses))));calling from binding form OK!
The result of executing the in the repl is:
2) from fetch-expenses-greater-than: nil
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil
If I change the definition of fetch-all-expenses to
(defn ^:dynamic fetch-all-expenses [])
The result is as expected:
2) from fetch-expenses-greater-than: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil
It is possible to make a Var dynamic after it's been defined, but this will have no effect on code compiled before this change (it will still use the root binding). Use with-redefs to install custom replacements for functions during tests and the like.
The reason for this is that whether a Var is marked dynamic or not determines the way code using this Var is compiled. If it is not dynamic, such code will just get the root binding directly, saving some work; if it is dynamic, it will go through the somewhat more complex process of checking whether there is a thread-local binding in place for it.
So, there is no way to cause already compiled code use a custom function installed with binding after marking the Var holding the function dynamic. However, those calls still go through the Var, they just happen to go directly to the root binding, so you can use custom replacement functions for testing and the like if you install them as root bindings for the appropriate Vars. with-redefs encapsulates all the necessary logic for doing this cleanly.
Let's see how this works at the REPL:
;; define a non-dynamic Var:
(def foo 0)
;; this will throw an exception complaining about the attempt
;; to bind a non-dynamic Var:
(binding [foo 1]
foo)
;; let's define a function using foo;
;; we'll use it further down:
(defn bar []
foo)
;; now let's mark the Var dynamic:
(.setDynamic #'foo)
;; this will now evaluate to 1:
(binding [foo 1]
foo)
;; however, this will still return 0:
(binding [foo 1]
(bar))
(def ^{:dynamic true} log-call #'log-call) This statement says: "Create a var log-call and bind it to the var log-call. So when you try to use log-call var it will keep refering itself forever and hence StackOverflow exception.
You can try something like this:
(defn wrap [f]
(fn [& args] (apply f args)))
(def ^{:dynamic true} log-call (wrap log-call))
(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))
Thank you very much for your answer #MichaƂMarczyk. That explains it.
With code using the var before making it dynamic:
(def foo 0)
(defn bar []
foo)
(.setDynamic #'foo)
(binding [foo 1]
;; prints foo 1 . bar 0
(println "foo" foo ". bar" (bar)))
With code using the var after making it dynamic:
(def foo 0)
(.setDynamic #'foo)
(defn bar []
foo)
(binding [foo 1]
;; prints foo 1 . bar 1
(println "foo" foo ". bar" (bar)))
And yes!....with with-redefs instead of binding everything works as expected. That's exactly what I was needing.

clojure - eval code in different namespace

I'm coding something like REPL Server. Request from users evaluates in such function:
(defn execute [request]
(str (try
(eval (read-string request))
(catch Exception e (.getLocalizedMessage e)))))
Each client in separate thread. But they have the same namespace. How can I run code in dynamic created namespace ? So when new client connected, I want to create new namespace and to run client handling loop code there. Or maybe it's possible to run (eval ..) in other namespace ?
Thanks.
upd.
Solved!
Execute function:
(defn execute
"evaluates s-forms"
([request] (execute request *ns*))
([request user-ns]
(str
(try
(binding [*ns* user-ns] (eval (read-string request)))
(catch Exception e (.getLocalizedMessage e))))))
Each client gets it's own namespace by:
(defn generate-ns
"generates ns for client connection"
[] (let [user-ns (create-ns (symbol (str "client-" (Math/abs (.nextInt random)))))]
(execute (str "(clojure.core/refer 'clojure.core)") user-ns)
user-ns))`
(defn delete-ns
"deletes ns after client disconnected"
[user-ns] (remove-ns (symbol (ns-name user-ns))))
offtop: How to make offsets in code snippets on begin of line ?
Solved:
(binding [*ns* user-ns] (eval (read-string request)))
(symbol (str "client-" (Math/abs (.nextInt random)))
I just wanted to add, that this could be achieved with
(gensym "client-")
(I wanted to comment, but it turns our that I can't :))
Changing namespace means that you will have to reinitialize all the aliases, or refer to even clojure.core stuff with a fully qualified name:
user=> (defn alien-eval [ns str]
(let [cur *ns*]
(try ; needed to prevent failures in the eval code from skipping ns rollback
(in-ns ns)
(eval (read-string str))
(finally
(in-ns (ns-name cur))
(remove-ns ns))))) ; cleanup, skip if you reuse the alien ns
#'user/alien-eval
user=> (alien-eval 'alien "(clojure.core/println clojure.core/*ns*)") ; note the FQN
#<Namespace alien> ; the effect of println
nil ; the return value of alien-eval
You can write a macro that mimics
(defmacro my-eval [s] `~(read-string s))
It works better that eval because the symbol resolution of s occurs in the context that calls my-eval. Thanks to #Matthias Benkard for the clarifications.