2009-10-27 7 views
2

Я работаю с инструментарием обмена сообщениями (это бывает Spread, но я не знаю, что детали имеют значение). Для получения сообщений из этого инструментария требуется некоторый шаблон:Преобразуйте этот вызов Clojure в ленивую последовательность

  1. Создайте соединение с демоном.
  2. Присоединиться к группе.
  3. Получалось одно или несколько сообщений.
  4. Оставьте группу.
  5. Отключить от демона.

После некоторых идиом, которые я видел, используемых elsewhere, я был в состоянии приготовить некоторые рабочие функции с использованием Spread в Java API и Interop формы Clojure в:

(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))) 

(в основном тот же идиома, как with-open, только что SpreadConnection класс использует disconnect вместо close. Grr. Кроме того, я оставил некоторые макросы, которые не относятся к структурному вопрос здесь.)

Это работает достаточно хорошо. Я могу назвать приемно-сообщение внутри структуры, как:

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

Это происходит со мной, что receive-message будет чист использовать, если бы бесконечная последовательность ленивой, которая производит сообщения. Так, если бы я хотел, чтобы присоединиться к группе и получить сообщения, вызывающий код должен выглядеть примерно так:

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

Я видел много примеров ленивых последовательностей без очистки, это не слишком сложно. Захват - это шаги № 4 и 5 сверху: оставление группы и отключение от демона. Как я могу связать состояние соединения и группы с последовательностью и выполнить необходимый код очистки, когда последовательность больше не нужна?

ответ

6

This В статье описывается, как это сделать, используя clojure-contrib fill-queue. Что касается очистки - аккуратная вещь о заполнении очереди заключается в том, что вы можете предоставить функцию блокировки, которая очистится, если есть ошибка или какое-то условие достигнуто. Вы также можете использовать ссылку на ресурс, чтобы управлять им извне. Последовательность просто прекратится. Поэтому в зависимости от вашего семантического требования вам придется выбирать стратегию, которая подходит.

3

Попробуйте это:

(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)))))) 

Set сделано? на true, когда вы хотите завершить список. Кроме того, любые исключения, заброшенные в (.receive cnx), также завершат список.