I want a map in an atom that can keep track of times as Unix time stamps.
So, in my main function I have:
(defn -main [& args]
(println "Server is starting")
(def port (Integer/parseInt (first args)))
(def registry (atom {}))
(run-server port who-is-here registry))
And inside of run-server I have a call to add-to-logged-in-registry:
(defn add-to-logged-in-registry
[registry]
(let [moments (Date.)
right-now (.getTime moments)]
(swap! registry conj right-now)))
This last line gives me this error:
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
at clojure.lang.RT.seqFrom(RT.java:487)
at clojure.lang.RT.seq(RT.java:468)
at clojure.lang.APersistentMap.cons(APersistentMap.java:39)
at clojure.lang.RT.conj(RT.java:544)
at clojure.core$conj.invoke(core.clj:83)
at clojure.lang.Atom.swap(Atom.java:51)
at clojure.core$swap_BANG_.invoke(core.clj:2107)
at who_is_logged_in.core$add_to_logged_in_registry.invoke(core.clj:39)
at who_is_logged_in.core$listen_and_respond.invoke(core.clj:42)
at who_is_logged_in.core$run_server.invoke(core.clj:52)
at who_is_logged_in.core$_main.doInvoke(core.clj:76)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at who_is_logged_in.core.main(Unknown Source)
What does this mean?
When I try this at the REPL in emacs, this works perfectly:
user> (def registry (atom []))
#'user/registry
user> (let [moments (Date.)
right-now (.getTime moments)]
(swap! registry conj right-now))
[1345698128988]
user> (let [moments (Date.)
right-now (.getTime moments)]
(swap! registry conj right-now))
[1345698128988 1345698132472]
conj behaves differently depending on the type of collection it is adding elements to. In your first example it is adding elements to a map and needs A key and a value in a collection. In your REPL example it is adding elements to a vector and needs only a single value.
swap!ing into a map:
(def registry (atom{}))
(let [moments (java.util.Date.)
right-now (.getTime moments)]
(swap! registry conj [:time right-now]))
{:time 1345700872898}
Related
I've been struggling to create a macro that'll allow me to dynamically bind whatever is in the &env into a binding form and then delegate to a pry like function to open a REPL that can see those bound &env symbols.
My simplistic pry func, which works as expected
(defn pry []
(print (str "pry(" *ns* ")> "))
(flush)
(let [expr (read)]
(when-not (= expr :exit)
(println (eval expr))
(recur))))
Using the pry func:
clojure-noob.core=> (def a 1)
#'clojure-noob.core/a
clojure-noob.core=> (pry)
pry(clojure-noob.core)> (+ a 1)
2
pry(clojure-noob.core)> :exit
nil
clojure-noob.core=>
My attempt at creating a dynamic invocation of binding:
(defmacro binding-pry []
(let [ks (keys &env)]
`(let [ks# '~ks
vs# [~#ks]
bs# (vec (interleave ks# vs#))]
(binding bs# (pry)))))
However, this fails because the inner symbol bs# is not expanded to an actual vector but instead is the generated symbol and binding tosses a clojure.core/binding requires a vector for its binding exception.
clojure-noob.core=> (let [a 1 b 2] (binding-pry))
Syntax error macroexpanding clojure.core/binding at (/tmp/form-init14332359378145135257.clj:1:16).
clojure.core/binding requires a vector for its binding in clojure-noob.core:
clojure-noob.core=>
The code quoted form with a debug print, the bs# symbol is resolved when printing but I don't know how to make it resolve to a vector when constructing the binding form.
(defmacro binding-pry []
(let [ks (keys &env)]
`(let [ks# '~ks
vs# [~#ks]
bs# (vec (interleave ks# vs#))]
(println bs#)
`(binding bs# (pry)))))
clojure-noob.core=> (let [a 1 b 2] (binding-pry))
[a 1 b 2]
(clojure.core/binding clojure-noob.core/bs__2464__auto__ (clojure-noob.core/pry))
clojure-noob.core=>
I'm very confident I'm tackling this incorrectly but I don't see another approach.
The Joy of Clojure demonstrates a break macro that does this already. I can't reproduce its source here, because it's EPL and not CC. But you can see its source at https://github.com/joyofclojure/book-source/blob/b76ef15/first-edition/src/joy/breakpoint.clj. It refers to a contextual-eval function as well: https://github.com/joyofclojure/book-source/blob/b76ef15/first-edition/src/joy/macros.clj#L4-L7.
A first step towards improving your attempt could be writing:
(defmacro binding-pry []
(let [ks (keys &env)]
`(binding [~#(interleave ks ks)] (pry))))
This still doesn't work because binding expects that the symbols can be resolved to existing dynamic vars. To tackle this problem you could make binding-pry introduce such vars as shown below:
(defmacro binding-pry []
(let [ks (keys &env)]
`(do
~#(map (fn [k] `(def ~(with-meta k {:dynamic true}))) ks)
(binding [~#(interleave ks ks)] (pry)))))
But this can have undesirable side-effects, like polluting the namespace with new var-names or making existing vars dynamic. So I would prefer an approach like the one mentioned in amalloy's answer but with a better implementation of eval-in-context (see my comment there).
To write a self-contained answer based on your pry function, let's first define eval-in which evaluates a form in an environment:
(defn eval-in [env form]
(apply
(eval `(fn* [~#(keys env)] ~form))
(vals env)))
Then let's modify pry to take an environment as an argument and use eval-in instead of eval:
(defn pry [env]
(let [prompt (str "pry(" *ns* ")> ")]
(loop []
(print prompt)
(flush)
(let [expr (read)]
(when-not (= expr :exit)
(println (eval-in env expr))
(recur))))))
An equivalent, less primitive version could be:
(defn pry [env]
(->> (repeatedly (let [prompt (str "pry(" *ns* ")> ")]
#(do (print prompt) (flush) (read))))
(take-while (partial not= :exit))
(run! (comp println (partial eval-in env)))))
Now we can define binding-pry as follows:
(defmacro binding-pry []
`(pry ~(into {}
(map (juxt (partial list 'quote) identity))
(keys &env))))
Finally, here is a direct/"spaghetti" implementation of binding-pry:
(defmacro binding-pry []
(let [ks (keys &env)]
`(->> (repeatedly (let* [prompt# (str "pry(" *ns* ")> ")]
#(do (print prompt#) (flush) (read))))
(take-while (partial not= :exit))
(run! (comp println
#((eval `(fn* [~~#(map (partial list 'quote) ks)] ~%))
~#ks))))))
I am working on updating counters in a map ref in Clojure.
(defn increment-key [this key]
(dosync
(let [value (get #this key)]
(if (= value nil)
(alter this assoc key (ref 1))
(alter this assoc key (alter value inc))))))
However, it looks like the alter value inc statement is losing the reference:
(defn -main [& args]
(def my-map (ref {}))
(increment-key my-map "yellow")
(println my-map)
(increment-key my-map "yellow")
(println my-map))
Which prints:
$ lein run
#<Ref#65dcc2a3: {yellow #<Ref#3e0d1329: 1>}>
#<Ref#65dcc2a3: {yellow 2}>
How can I keep the same reference while updating it in this scenario?
You were almost there. Below is the solution, check the last line of increment-key, you just need to alter the value (not alter the key in the map as you were doing, coz that was causing the key to be updated with the alter return value which in you example was 2, remember alter returns the new value of the ref, not the ref itself). Also don't use def inside a def, you should use let (in your -main function)
(defn increment-key [this key]
(dosync
(let [value (get #this key)]
(if (= value nil)
(alter this assoc key (ref 1))
(alter value inc)))))
(defn -main [& args]
(let [my-map (ref {})]
(increment-key my-map "yellow")
(println my-map)
(increment-key my-map "yellow")
(println my-map)))
I apparently do not understand clojure's atom correctly. I thought that it's atomicity guarantee could be demonstrated as follows:
(def users (atom #{}))
(defn add-user! [name]
(swap! users
(fn [users]
(conj users name))))
(do
(map deref
[(future (add-user! "bob"))
(future (add-user! "clair"))
(future (add-user! "ralph"))
(future (add-user! "mark"))
(future (add-user! "bill"))
(future (add-user! "george"))]))
(println #users)
(println
(if (= 5 (count #users))
"SUCCESS!"
"FAIL"))
Unfortunately this is not the case. The code seems to exhibit a race condition on the set contained in the users atom.
Which data structure do I need to use to make sure that all users are successfully added to the users set?
SOLUTION
As pointed out in the comments, there were several bugs in the code. The main bug was not using dorun to force the evaluation of all of the futures. After making this change, the code runs as expected:
(def users (atom #{}))
(defn add-user! [name]
(swap! users
(fn [users]
(conj users name))))
(dorun
(map deref
[(future (add-user! "bob"))
(future (add-user! "clair"))
(future (add-user! "ralph"))
(future (add-user! "mark"))
(future (add-user! "bill"))
(future (add-user! "george"))]))
(println #users)
(println
(if (= 6 (count #users))
"SUCCESS!"
"FAIL"))
See Clojure Atom documentation.
Also from Joy of Clojure:
Atoms are like Refs in that they're synchronous but are like Agents in that they're independent (uncoordinated).
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)
So, in my core.clj file I have:
(def page-buffer (BufferedReader. (InputStreamReader. (clojure.java.io/input-stream (clojure.java.io/resource "mitochondria.html")))))
(def parsed-page (atom ""))
and then later:
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(println "Server is starting")
(println "port: " port)
(run-server port)))
This compiles and then I turn it into an uberjar. But when I run it I get the line with swap! blows up:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn
at clojure.core$swap_BANG_.invoke(core.clj:2106)
at serve_pages_from_memory.core$_main.doInvoke(core.clj:29)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at serve_pages_from_memory.core.main(Unknown Source)
I tried this without using an atom and everything worked just fine (using a var defined with "def" to be a string) but eventually I want to send this var to multiple threads, so I need for this to be an atom or agent.
What have I done wrong?
UPDATE:
Jeremy Heiler, thanks. Fixed, but the result is ugly as sin:
(defn parse-buffer [& everything-else]
(with-open []
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq page-buffer)))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page parse-buffer)
(println "Server is starting")
(println "port: " port)
(run-server port)))
I have to give an argument to parse-buffer or I get an error. But I do not use the argument, so this is ugly. I must be writing this wrong, yes?
You need to pass a function to swap!. Right now you are passing it a value.
(let [foo (atom 1)]
(swap! foo + 2)
#foo)
The above expression will return 3. The passed in function takes the current value of the atom, and the return value becomes the new value of the atom. Any extra arguments are passed to the given function.
To comment on your update: Yes, you do need to give an argument to the function used in swap!. The first argument is the current value of the the atom. However, you don't need to hack parse-buffer in order to make it work. You can just wrap it with an anonymous function.
Also, parse-buffer can be simplified greatly. Since line-seq returns a sequence of lines from the given reader, and str uses a StringBuilder internally, you can just apply it over the sequence.
(defn parse-buffer []
(with-open [buf page-buffer]
(apply str (line-seq buf))))
(defn -main [& args]
(let [port (Integer/parseInt (first args))]
(swap! parsed-page (fn [cur-val] (parse-buffer)))
(println "Server is starting")
(println "port: " port)
(run-server port)))