I am reading a book to teach myself Clojure called Clojure for the Brave and True. Chapter 9 covers basic concurrent programming including delays, futures, and promises. The first exercise at the end of the chapter states:
"Write a function that takes a string as an argument and searches for it on Bing and Google using the slurp function. Your function should return the HTML of the first page returned by the search"
My solution is as follows:
(defn search-bing-google
[search-term]
(let [search-results (promise)]
(future (deliver search-results
(slurp (str "https://www.bing.com/search?q%3D" search-term))))
(future (deliver search-results
(slurp (str "https://www.google.com/search?q%3D" search-term))))
#search-results))
And can be called like:
(search-bing-google "clojure")
The second exercise is stated as:
"Update your function so it takes a second argument consisting of the search engines to use."
I attempted to edit my first solution to meet the new argument requirement as follows:
(def search-engines
{:bing "https://www.bing.com/"
:google "https://www.google.com/"})
(defn search
[search-term & engines]
(let [results (promise)]
(map #(future (deliver results
(slurp (str (% search-engines)
"search?q%3D"
search-term)))) engines)
#results))
and can be called like:
(search "clojure" :bing :google)
However this implementation hangs unlike it's predecessor. Its as if the slurp never gets invoked because of the "map" in the second implementation. Can anyone help me figure out what's causing this to hang when I load it up in the REPL and execute it?
EDIT:
With Josh's answer below I came up with the following solution that no longer hangs using doseq instead of dorun and a map:
(def search-engines
{:bing "https://www.bing.com/"
:google "https://www.google.com/"})
(defn search
[search-term & engines]
(let [results (promise)]
(doseq [engine engines]
(future (deliver results
(slurp (str (engine search-engines)
"search?q%3D"
search-term)))))
#results))
Because map results in lazy evaluation, something is required in order to realize it. In your code, nothing does this, so the futures are never actually created. Instead of just map, do:
(dorun (map ...
Related
I'm currently in the start of making a tool that should modify all the functions in another namespace, and the run the "main" function in that (other) namespace. Almost like mocking in unit-testing, but for another purpose. The first step to this is to redefine functions from another namespace. The first listing shows code that works, where I explicitly name the function to be replaced.
(ns one)
(defn a-fun []
(println "original"))
(defn caller []
(a-fun))
(ns two)
(with-redefs [one/a-fun #(println "replaced")]
(one/caller))
;; replaced
;; nil
The next step is getting the functions programmatically from the other namespace. This is where I have run into a wall. I've experimented much with ns-publics, but so far no luck. So I try to break the problem down. And the problem is that the replacement of the function is not working. The output should print "replaced" not "original".
(ns two)
(def target (ns-resolve 'one (symbol "a-fun")))
(def caller (ns-resolve 'one (symbol "caller")))
(with-redefs [target #(println "replaced")]
(caller))
;; original
;; nil
Now, the #'two/target shows #'one/a-fun when I evaluate it in the repl. I can call one/a-fun by entering ((ns-resolve 'one (symbol "a-fun"))) in the repl, so I don't understand what part is not working.
I've been through a lot of documentation today, and I'm not really getting any closer.
You could try using with-redefs-fn like so:
(defn p [] "old")
(with-redefs-fn
{(ns-resolve *ns* 'p) #(println "new")}
(fn []
(p)))
;; => new
This does mean that the body of with-redefs-fn must be a function that uses your redefined Vars.
Is it ok to use binding with core.async? I'm using ClojureScript so core.async is very different.
(def ^:dynamic token "no-token")
(defn call
[path body]
(http-post (str host path) (merge {:headers {"X-token" token}} body)))) ; returns a core.async channel
(defn user-settings
[req]
(call "/api/user/settings" req))
; elsewhere after I've logged in
(let [token (async/<! (api/login {:user "me" :pass "pass"}))]
(binding
[token token]
(user-settings {:all-settings true})))
In ClojureScript1, binding is basically with-redefs plus an extra check that the Vars involved are marked :dynamic. On the other hand, gos get scheduled for execution1 in chunks (that is, they may be "parked" and later resumed, and interleaving between go blocks is arbitrary). These models don't mesh very well at all.
In short, no, please use explicitly-passed arguments instead.
1 The details are different in Clojure, but the conclusion remains the same.
2 Using the fastest mechanism possible, setTimeout with a time of 0 if nothing better is available.
I'm currently reading the Clojure Programming book, and following the examples. However I come across an error.
(defn print-logger
[writer]
#(binding [*out* writer]
(println %)))
(def *out*-logger (print-logger *out*))
(*out*-logger "hello")
Will result in: `Can't dynamically bind non-dynamic var: user/out
I'm very new to Clojure, and don't understand why this happens, especially when I'm following the example :)
The issue is not in the code sample you have.
*out* should refer to clojure.core/*out*, so if you are running this in a repl you might have run something previous, to what you have, like (def *out* something) to create a user/*out*.
Then, when you defined your print-logger function, the (binding [*out* writer] ...) statement would be trying to rebind user/*out* instead of clojure.core/*out*.
You can use ns-unmap to remove user/*out* from your namespace.
(ns-unmap 'user '*out*) ;; => nil
You will also need to define your print-logger function again to recapture the correct clojure.core/*out*.
I have a piece of code which I run in parallel using a future -
(def services [s1 s2])
(defn get-data []
(->>
(map #(future
(->> (p/fetch %)
(p/parse %)))
services)
(map deref)
(apply concat)))
(get-data)
The requirement for the above piece of code is to run two services in parallel. So I want to create a test that determines if the function (get-data) runs code for both the services in parallel or not. How do write such a test?
Use an agent to record the start and stop events into a sequence, then read the sequence to ensure that it contains at least two start events before the first stop event.
(map #(future
(send watcher conj :start)
(let [result (->> (p/fetch %)
(p/parse %))]
(send watcher conj :finish)
result)))
or you can use with-redefs to wrap your fetch and parse functions to avoid mixing test and production code.
How to achieve Aspect-Oriented Programming in Clojure? Do we need AOP in Clojure?
Let's say we want plain vanilla Clojure solution (no AspectJ).
Aspect-Oriented Programming is typically used to add cross-cutting functionality to code that would otherwise get hopelessly intertwined with business logic. A great example is logging - you don't really want logging code scattered everywhere in your code base.
You don't really need AOP in Clojure because it's easy to achieve this with other techniques in Clojure.
For example, you can use higher-order functions to "wrap" other functions with cross cutting functionality:
; a simple function - the "business logic"
(defn my-calculation [a b]
(+ a b))
; higher order function that adds logging to any other function
(defn wrap-with-logging [func]
(fn [& args]
(let [result (apply func args)]
(println "Log result: " result)
result)))
; create a wrapped version of the original function with logging added
(def my-logged-calculation (wrap-with-logging my-calculation))
(my-logged-calculation 7 9)
=> Log result: 16
=> 16
AOP IMHO is just an artifact of certain kinds of static programming languages. AFAIKS it's usually just a bunch of non-standard compiler extensions. I've not yet seen any application of AOP that can't be solved better & natively in more dynamic languages. Clojure is certainly dynamic enough, and that's without even considering macros.
I may be wrong, but if so, I'd need to see an actual AOP use case that can't be implemented just as well in pure clojure.
Edit: just to be clear: I refuse to see things like elisp's advice as aspect oriented. In dynamic languages, those are just techniques to be used whenever you need them, with no need for language support other than rebinding of function definitions - which all lisps support anyway.
There's no need to treat them as special - you can easily define your own defadvice-like function in clojure. See for example, compojure's wrap! macro, which is actually deprecated since you generally don't even need it.
Aspect oriented programming is a great way to achieve seperation of concernes in Java. Clojure's composable abstractions achieve this very well. See this question also. The topic is covered really well in The Joy Of Clojure.
as for an example of Aspect Oriented Clojure by another name check out the Ring web framework
Well you could be more AOP w/ Clojure easily. Just use metadata in functions to informe when you want logs:
(defn ^:log my-calculation
[a b]
(+ a b))
Then you can redefine all functions, wrapping them w/ logging automatically. Part of this code (together w/ unwrap functions bellow):
(defn logfn
[f topic severity error-severity]
(fn [& args]
(try
(if severity
(let [r (apply f args)]
(log* topic {:args args, :ret r} severity)
r)
(apply f args))
(catch Exception e
(if error-severity
(let [data {:args args, :error (treat-error e), :severity error-severity}]
(log* topic data error-severity)
(throw e))
(throw e))))))
(defn logfn-ns
"Wrap function calls for logging on call or on error.
By default, do nothing. When any :log or :log-error, enables logging. If ^:log,
only log on error (default severity error).
Can customize log severity w/ e.g. ^{:log info} or on error log severity likewise."
[ns alias]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)
f #v
log (-> v meta :log)
log-error (-> v meta :log-error)]
:when (and (ifn? f)
(-> v meta :macro not)
(-> v meta :logged not) ;; make it idempotent
(or log log-error))]
(let [log (if (= log true) nil log)
log-error (or log-error "error")
f-with-log (logfn f
(str alias "/" s)
log
log-error)]
(alter-meta! (intern ns s f-with-log)
(fn [x]
(-> x
(assoc :logged true)
(assoc :unlogged #v)))))))
(defn unlogfn-ns
"Reverts logfn-ns."
[ns]
(doseq [s (keys (ns-interns ns))
:let [v (ns-resolve ns s)]
:when (-> v meta :logged)]
(let [f-without-log (-> v meta :unlogged)]
(alter-meta! (intern ns s f-without-log)
(fn [x]
(-> x
(dissoc :logged)
(dissoc :unlogged)))))))
You just call (log/logfn-ns 'my.namespace "some alias") and all is wrapped w/ logging (and some).
PS: My custom logger above have a topic which is "some alias/function name"
PS2: Also wrapped w/ try/catch.
PS3: Didn't like this so much. Reverted to have explicit logging.