Transformar esta chamada Clojure numa sequência preguiçoso
-
06-07-2019 - |
Pergunta
Eu estou trabalhando com um conjunto de ferramentas de mensagens (que passa a ser Espalhe mas eu não sei que os detalhes são importantes). Recebendo mensagens deste kit de ferramentas requer algum clichê:
- Criar uma conexão com o daemon.
- Participe de um grupo.
- Receber uma ou mais mensagens.
- Deixe o grupo.
- Desconectar do daemon.
Na sequência de algumas expressões idiomáticas que eu vi usado outro lugar , eu era capaz de cozinhar algumas funções de trabalho que utilizam API Java de propagação e as formas de interoperabilidade do 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)))
(Basicamente o mesmo idioma como with-open
, só que os usos de classe SpreadConnection
disconnect
vez de close
. Grr. Além disso, eu deixei de fora algumas macros que não são relevantes para a questão estrutural aqui.)
Isso funciona bem o suficiente. Eu posso chamar receber-mensagem de dentro de uma estrutura como:
(with-daemon {:host "localhost" :port 4803}
(with-group "aGroup"
(... looping ...
(let [msg (receive-message)]
...))))
Ocorre-me que receive-message
seria mais limpo para uso se fosse uma seqüência preguiçoso infinito que produz mensagens. Então, se eu queria participar de um grupo e receber mensagens, o código de chamada deve ser algo como:
(def message-seq (messages-from {:host "localhost" :port 4803} "aGroup"))
(take 5 message-seq)
Eu vi muitos exemplos de seqüências preguiçosos, sem limpeza, que não é muito difícil. O prendedor é passos # 4 e 5 a partir de cima: deixando o grupo e desligar do daemon. Como posso vincular o estado da conexão e grupo na seqüência e executar o código de limpeza necessária quando a seqüência não é mais necessária?
Solução
Este artigo descreve como fazer exatamente isso usando clojure-contrib fill-fila. Em relação à limpeza - a coisa pura sobre fill-fila é que você pode fornecer uma função de bloqueio que se limpa-se se houver um erro ou alguma condição alcançada. Você também pode manter uma referência para o recurso para controlá-lo externamente. A sequência será apenas terminar. Então, dependendo de sua exigência semântica você terá que escolher a estratégia que se encaixa.
Outras dicas
Tente isto:
(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 feito? a verdade quando você quer terminar a lista. Além disso, quaisquer exceções lançadas em (CNX .receive) também irá terminar a lista.