Hi, I'm not sure my solution is 100% idiomatic, but has some advantages. General strategy: minimise macro usage. Macros are cool, but overrated. Put as much as possible into functions. This has several advantages like easier testing, less macro trap doors, better readability (no # all over the place) or possibility of hotfixing of eg. the cleanup code in with-context[1], etc.
So my plan is: put everything in special global variables. Expose them as options for the functions. The defaults refer to the global variables which are set with binding. So allows for usage pattern, but could also allow for easy testing. (with-context [:some option] (init-source "bla" :some "option")) or (init-source "bla" :some "option" :context manually-crafted-context) Here is my try: (declare *context* *thread* *receivers* *sources*) (defn init-receiver [topic & {:keys [context receivers] :or {context *content* receivers *receivers*} :as options}] ...boring java stuff) (defn init-source [topic & {:keys [context sources] :or {context *content* sources *sources*} :as options}] ...boring java stuff) (defn publish [topic message & {:keys [context sources] :or {context *content* sources *sources*} :as options}] ...boring java stuff) (defn receive [topic func & {:keys [context receivers] :or {context *content* receivers *receivers*} :as options}] ...boring java stuff) (defn with-context* [options thunk] (let [context (create-context options) thread (create-thread context) sources (atom {}) receivers (atom {})] (binding [*context* context *thread* thread *sources* sources *receivers* receivers] (try (thunk) (finally ... close stuff, cleanup))))) (defmacro with-context [options & body] `(with-context* ~options (fn [] ~...@body))) Icying (not tested though, but some quick macroexpands seem to be promising): (defmacro defsugaredfn [fnname & fntail] (let [[docstring fntail] (let [[docstring & tail :as fntail] fntail] (if (string? docstring) [docstring tail] [nil fntail])) [metamap fntail] (let [[metamap & tail :as fntail] fntail] (if (map? metamap) [metamap tail] [nil fntail])) args (first fntail) fntail (next fntail) options (peek args) options (when (map? options) options) mod-options (merge-with into `{:keys [~'context ~'receivers ~'sources] :or {~'context *context* ~'receivers *receivers* ~'sources *sources*} :as ~'options} options) args (if options (pop args) (conj args '&)) args (conj args mod-options)] `(defn ~fnname ~@(concat (when docstring [docstring]) (when metamap [metamap]) [args]) ~...@fntail))) Use as: (defsugaredfn init-receiver "with docstring! yeah!" [topic & {:keys [some] :or {some "option"}}] ...boring java stuff) or (defsugaredfn init-receiver [topic] ...boring java stuff) context, receivers, source and options will be captured this way and available in the "boring java stuff" part. Another downside: you have to take care when you cross thread boundaries. Then you have to use bound-fn or pass the context and friend explicitely. (with-context [:some "option"] ... (bound-fn [] (init-receiver "in another thread")) ... (fn [] (init-receiver "in another thread" :context context :receivers receivers) ,... ) Hope that helps. Sincerely Meikel [1]: With a macro the client code has to be recompiled. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en