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]
Related
Let's consider the following functions:
(defun test (x)
"X."
(request "http://example.com"
:parser 'json-read
:complete (cl-function
(lambda (&key response &allow-other-keys)
(message "%s" x)))))
(defun test2 (x)
"X."
(funcall (cl-function (lambda (z) (message "%s" z))) x))
Calling (test2 3) Works fine and produces the desired message. Calling (test 3), however, fails with the following error:
error in process sentinel: let*: Symbol’s value as variable is void: x
error in process sentinel: Symbol’s value as variable is void: x
My guess is that request is a macro doing something weird to variable scoping. Unfortunately, the documentation does not mention anything like this. Is there a way to overcome that?
request is probably an ordinary function.
The problem is that the lambda you pass to it is not called immediately but is
saved and called later:
(defun make-function (x)
(lambda () (message "received %s" x)))
=> make-function
(defconst f (make-function 3))
=> f
(funcall f)
=> Lisp error: (void-variable x)
x that make-function bound no longer exists.
This is because by default Emacs uses dynamic binding and you need lexical binding to get the behavior you want.
If you add
;; -*- lexical-binding:t -*-
to the first line of your file, it will be compiled with lexical binding and
the code above will produce the message received 3 instead of an error.
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).
My main goal is to redirect stderr to a file.
I got hold of the following code snippet...
catchOutput :: IO a -> IO (res, String)
catchOutput f = do
tmpd <- getTemporaryDirectory
(tmpf, tmph) <- openTempFile tmpd "haskell_stderr"
stderr_dup <- hDuplicate stderr
hDuplicateTo tmph stderr
hClose tmph
res <- f
hDuplicateTo stderr_dup stderr
str <- readFile tmpf
removeFile tmpf
return (res, str)
I hoped to make this more general and pass any function and argument list to catchOutput and get the function result as well as message written to stderr (if any).
I thought that an argument list of type [Data.Dynamic] might work but I failed to retrieve the function result with
res <- Data.List.foldl (f . fromDyn) Nothing $ args
Is this even possible? Help will be greatly appreciated.
There is not reason to use Data.Dynamic. You already know type the return type of f, it's a so you can use just that, i.e.
catchOutput :: IO a -> IO (a, String)
Note though, that there some significant issues with your approach:
By redirecting stderr to a file, this will also affect all other concurrent threads. So you could possibly get unrelated data sent to the temporary file.
If an exception is thrown while stderr is redirected, the original stderr will not be restored. Any operation between the two hDuplicateTo lines (hClose and f in this case) could possibly throw an exception, or the thread may receive an asynchronous exception. For this reason, you have to use something like bracket to make your code exception safe.
I've started to learn clojure. In my book there is following exercise:
Write a function, mapset, that works like map except the return value is a set:
(mapset inc [1 1 2 2])
; => #{2 3}
I've started with something like this:
(defn mapset
[vect]
(set vect))
The result is error
"Wrong number of args (2) passed to: core/mapset"
I tried [& args] as well.
So, the question is: how can I solve such problem?
Take a closer look at your call to mapset:
(mapset inc [1 1 2 2])
Since code is data, this "call" is just a list of three elements:
The symbol mapset
The symbol inc
The vector [1 1 2 2]
When you evaluate this code, Clojure will see that it is a list and proceed to evaluate each of the items in that list (once it determines that it isn't a special form or macro), so it will then have a new list of three elements:
The function to which the symbol core/mapset was bound
The function to which the symbol clojure.core/inc was bound
The vector [1 1 2 2]
Finally, Clojure will call the first element of the list with the rest of the elements as arguments. In this case, there are two arguments in the rest of the list, but in your function definition, you only accounted for one:
(defn mapset
[vect]
(set vect))
To remedy this, you could implement mapset as follows:
(defn mapset
[f vect]
(set (map f vect)))
Now, when you call (mapset inc [1 1 2 2]), the argument f will be found to the function clojure.core/inc, and the argument vect will be bound to the vector [1 1 2 2].
Your definition of mapset takes a single argument vect
At a minimum you need to take 2 arguments, a function and a sequence
(defn mapset [f xs] (set (map f xs)))`
But it is interesting to think about this as the composition of 2 functions also:
(def mapset (comp set map))
I am learning Clojure and trying to implement a simple tic-tac-toe (or morpion). But i am working hard in order to avoid any ref/atom/agent ...
I know I could do it easily in a console application, as it would not be event-driven, so that I would exactly know when to pass in a new board value. I mean
my board would be a vector of vectors ([[:circle 0 :cross][:any :circle :empty][:cross :none :none]] where only :circle and :cross values matter)
I would have a simple text-based method which takes a board and return a string
But, whenever I want to implement a graphical Panel, I wonder how can I do the same thing. For example :
I am creating an instance of javax.swing.JPanel (please don't care about the other methods that are used here) :
(defn make-board-panel
"Builds the board JPanel, whose state is given by board argument,
and turn-piece argument (must be either :circle or :cross, I mean the next
value to set in the board)."
[board turn-piece]
{:pre ['morpion.core/is-board? board, 'morpion.core/is-a-player-piece? turn-piece]}
(proxy [JPanel MouseListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint-board-lines g)
(paint-board board g)
)
(mouseClicked [e]
(if (and (abs-coord-in-cell? (.getX e)) (abs-coord-in-cell? (.getY e)))
(let [cell-x (abs-coord-to-rel (.getX e))
cell-y (abs-coord-to-rel (.getY e))]
;; Here I compute the new board value
)
nil ;; Here I wish I could return the new board value to the caller of make-board-panel, but this seems impossible !
)
)
(mouseEntered [e])
(mouseExited [e])
(mousePressed [e])
(mouseReleased [e])
)
)
But it seems that there are no way for me to fetch the new value of the board, from the mouseClicked event of the Panel : unless I introduce a state variable.
So is there a workaround :
Sources for my complete project :
Morpion Core
Morpion Graphic.
(I've tried to improve thanks to Igrapenthin comment, but I am still failing.)
(defn make-board-panel
[board turn-piece output-fn]
(proxy [JPanel MouseListener] []
;; ...
(mouseClicked [e]
(when (and (abs-coord-in-cell? (.getX e))
(abs-coord-in-cell? (.getY e)))
(let [cell-x (abs-coord-to-rel (.getX e))
cell-y (abs-coord-to-rel (.getY e))]
;; Here I compute the new board value
(output-fn new-board-value))))
;; ...
))