Reputation: 309
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))
Upvotes: 2
Views: 173
Reputation: 4806
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 ...
Upvotes: 3