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

Reply via email to