Apr 10
The world as a sequence
Hey there! welcome to my new blog, after long time of blogsphere absence iv returned with a new self hosted blog that iv developed, this little project gave me a couple of insights that id like to share.
One of the main abstractions that Clojure uses is that of a Sequence, a
seq is an interface (a java one) that represents a logical immutable persistent list,
any data structure that expose this interface enables a vast number of core functions to our disposal.
Nothing up to this point should sound out of the ordinary except that the real fun begins when we define our own
sequences!
(defn- jar-stream-seq [stream]
(take-while #(not= nil %) (repeatedly #(.getNextEntry stream))))
; extract-entry emitted for brevity
(defn unjar [src dest]
(with-open [jar-stream (JarArchiveInputStream. (file-in-stream src))]
(let [dir (file dest)]
(doseq [entry (jar-stream-seq jar-stream)] (extract-entry entry dir jar-stream)))))
Another example is CouchDb to Clojure state syncronization, most applications that interacts with CouchDb (or any other persistent storage) hold some state that need to be updated in order to reflect lastest changes.
Instead of polling the DB we can (in the case of CouchDb) listen to a broadcasted via a url (http://localhost:5984/db/_changes?feed=continuous&heartbeat=1000):
{"seq":1,"id":"43badb35255a94b4ab5c3168553d1acc","changes":[{"rev":"2-8630b20788a2fd2618d85d2239a31f7f"}],"deleted":true}
{"seq":2,"id":"1ed79fc5a1ddfaaf08a73e68b1f5432e","changes":[{"rev":"2-a72a134b273510eaa247c142fc62fd51"}],"deleted":true}
{"seq":3,"id":"df76d2644cef31df9f9e4e6db29e614d","changes":[{"rev":"5-1bbc0c3f12cf3b3f23ef7f2fb99a6e8e"}]}
This channel publishes all the changes in a json format that we can consume (can you guess already how?):
(defn- bytes-seq [input]
(take-while #(not= -1 %) (repeatedly #(. input read))))
(defn- changes-seq []
(let [url (URL. (couch str "/_changes?feed=continuous&heartbeat=1000&since=" (last-seq)))
uc (. url openConnection)]
(. uc connect)
(filter (comp not blank?)
(map #(reduce (fn [r c] (str r c)) "" %)
(partition-by #(if (= \newline %) true)
(map char (bytes-seq (. uc getInputStream))))))))
(defn follow-couch-changes []
(doseq [change (changes-seq) :let [id (read-id change)]]
(when (not-design-id id)
(dosync
(alter posts-map assoc id (read-with-date id))
(alter posts init-posts)))))
The bytes-seq gives a bytes sequence that is mapped into chars and partitioned into lines under changes-seq,
note that since we use heartbeat of 1000 millis (keeping the connection open) we get empty lines that are filtered
out, the processing of these events is made using the standard Clojure functions.
Another important aspect of this is that we can process

Follow me online