Convert this Clojure call to lazy sequence

I'm working with a messaging toolkit (it happens to be Spread , but I don't know what the details matter). Getting messages from this toolkit requires some boilerplate:

  • Create a connection to the daemon.
  • Join the group.
  • Receive one or more messages.
  • Leave the group.
  • Disconnect from the demon.

Following some idioms I saw, I used elsewhere , I was able to get some working functionality to work with the Spread Java API and Clojure form interaction:

(defn connect-to-daemon
  "Open a connection"
  [daemon-spec]
  (let [connection (SpreadConnection.)
        {:keys [host port user]} daemon-spec]
    (doto connection
      (.connect (InetAddress/getByName host) port user false false))))

(defn join-group
  "Join a group on a connection"
  [cxn group-name]
  (doto (SpreadGroup.)
    (.join cxn group-name)))

(defn with-daemon*
  "Execute a function with a connection to the specified daemon"
  [daemon-spec func]
  (let [daemon (merge *spread-daemon* daemon-spec)
        cxn (connect-to-daemon daemon-spec)]
    (try
     (binding [*spread-daemon* (assoc daemon :connection cxn)]
       (func))
     (finally
      (.disconnect cxn)))))

(defn with-group*
  "Execute a function while joined to a group"
  [group-name func]
  (let [cxn (:connection *spread-daemon*)
        grp (join-group cxn group-name)]
    (try
     (binding [*spread-group* grp]
       (func))
     (finally
      (.leave grp)))))

(defn receive-message
  "Receive a single message. If none are available, this will block indefinitely."
  []
  (let [cxn (:connection *spread-daemon*)]
    (.receive cxn)))

      

(Basically the same idiom as and with-open

, only that the class SpreadConnection

uses disconnect

instead of close

. Grr. Also, I've left out some macros that are not relevant to the structural question here.)

This works well enough. I can call the message-to-message from within the structure, for example:

(with-daemon {:host "localhost" :port 4803}
  (with-group "aGroup"
    (... looping ...
      (let [msg (receive-message)] 
        ...))))

      

It seems to me that it receive-message

would be cleaner if it was an endless lazy sequence that creates messages. So, if I wanted to join the group and receive messages, the calling code should look something like this:

(def message-seq (messages-from {:host "localhost" :port 4803} "aGroup"))
(take 5 message-seq)

      

I've seen many examples of lazy sequences without cleanup, it's not too hard. Capture is steps # 4 and 5 from above: leaving the group and disconnecting from the demon. How do I associate the connection and group state with a sequence and run the necessary cleanup code when the sequence is no longer needed?

+2


source to share


2 answers


Try the following:

(ns your-namespace
  (:use clojure.contrib.seq-utils))

(defn messages-from [daemon-spec group-name]
  (let [cnx (connect-to-deamon daemon-spec))
        group (connect-to-group cnx group-name)]
    (fill-queue (fn [fill]
                  (if done? 
                      (do
                        (.leave group)
                        (.disconnect cnx)
                        (throw (RuntimeException. "Finished messages"))
                      (fill (.receive cnx))))))

      



Done? to true when you want to complete the list. Also, any exceptions thrown in (.receive cnx) will abort the list as well.

+3


source







All Articles