Refactoring redundancy out of Clojure code for RabbitMQ fanning - macros

I am running through the RabbitMQ tutorials on their web-page and as an exercise am trying to refactor the examples they provide to make them more general and composable. I am stuck on the second "blabbr" example. Here is the function I would like to refactor:
(defn -main
[& args]
(let [conn (rmq/connect)
ch (lch/open conn)
ex "nba.scores"
users ["joe" "aaron" "bob"]]
(le/declare ch ex "fanout" :durable false :auto-delete true)
(doseq [u users]
(start-consumer ch ex u))
(lb/publish ch ex "" "BOS 101, NYK 89" :content-type "text/plain" :type "scores.update")
(lb/publish ch ex "" "ORL 85, ALT 88" :content-type "text/plain" :type "scores.update")
(Thread/sleep 2000)
(rmq/close ch)
(rmq/close conn)))
I thought I could make a macro and call it in the function, like this:
(defmacro wrap-publish [default-exchange-name content mType data]
`(for [datum# data]
(lb/publish ch ex default-exchange-name datum# :content-type ~content :type ~mType)))
(defn -main
[...]
...
(wrap-publish default-exchange-name content-type mType data)
...
When I test the wrap-publish macro by itself at the repl, however, I get this error:
java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to` com.novemberain.langohr.Channel basic.clj:89 langohr.basic/publish
It seems that there is something global going on that won't let me bind my vars, but I have no idea what. I followed my nose to the source-code thrown at me from the stack-trace, and hit a dead end there. I just don't know what to do. I am a new programmer taking baby steps into the world of async and macros. So I would appreciate any advice that would help me to not only accomplish my goal but also provide general insights that will inform my basic skills and let me know if I am taking the right approach. I am using the langohr dependency [com.novemberain/langohr "2.9.0"].

As to the macro written
It looks like you are missing some unquotes in your macro definition. I don't use that library and you didn't provide an SSCCE, so I am advising without testing.
You macro should likely be
(defmacro wrap-publish [default-exchange-name content mType data]
`(doseq [datum# ~data]
(lb/publish ~'ch ~'ex ~default-exchange-name datum# :content-type ~content :type ~mType)))
Note the added ~' on ch and ex and the added ~ on data and default-exchange-name. Note also the change from for to doseq as for is lazy.
You would use like this
...
(let [...
ch ...
ex ...
...] ; end let bindings
...
(wrap-publish "" "text/plain" "scores.update" ["BOS 101, NYK 89", "ORL 85, ALT 88"]))
...
Since this produces code
(macroexpand-1 '(wrap-publish "" "text/plain" "scores.update" ["BOS 101, NYK 89", "ORL 85, ALT 88"]))
;=> (clojure.core/doseq [datum__1237__auto__ ["BOS 101, NYK 89" "ORL 85, ALT 88"]]
; (lb/publish ch ex "" datum__1237__auto__ :content-type "text/plain" :type "scores.update"))
containing the symbols ch and ex, they must be available in the let bindings.
As to the advise requested
There is really no good reason to write a macro here. If you are looking for advise, avoid writing macros altogether for your first 6-12 months with Clojure. Focus on functional programming skills first. After that, still avoid writing macros whenever possible!
The code that this macro produces should probably just be the code that you write:
(doseq [d ["BOS 101, NYK 89" "ORL 85, ALT 88"]]
(lb/publish ch ex "" d :content-type "text/plain" :type "scores.update"))
instead of messing with writing your own macros (doseq is macro itself).
If you need to do similar in multiple places, just define a function. If you have some context (e.g. ch and ex) that you don't care to repeat or that needs to escape its lexical scope, create a closure.

Related

Quote-unquote idiom in Julia & concatenating Expr objects

I'd like to write a simple macro that shows the names & values of variables. In Common Lisp it would be
(defmacro dprint (&rest vars)
`(progn
,#(loop for v in vars
collect `(format t "~a: ~a~%" ',v ,v))))
In Julia I had two problems writing this:
How can I collect the generated Expr objects into a block? (In Lisp, this is done by splicing the list with ,# into progn.) The best I could come up with is to create an Expr(:block), and set its args to the list, but this is far from elegant.
I need to use both the name and the value of the variable. Interpolation inside strings and quoted expressions both use $, which complicates the issue, but even if I use string for concatenation, I can 't print the variable's name - at least :($v) does not do the same as ',v in CL...
My current macro looks like this:
macro dprint(vars...)
ex = Expr(:block)
ex.args = [:(println(string(:($v), " = ", $v))) for v in vars]
ex
end
Looking at a macroexpansion shows the problem:
julia> macroexpand(:(#dprint x y))
quote
println(string(v," = ",x))
println(string(v," = ",y))
end
I would like to get
quote
println(string(:x," = ",x))
println(string(:y," = ",y))
end
Any hints?
EDIT: Combining the answers, the solution seems to be the following:
macro dprint(vars...)
quote
$([:(println(string($(Meta.quot(v)), " = ", $v))) for v in vars]...)
end
end
... i.e., using $(Meta.quot(v)) to the effect of ',v, and $(expr...) for ,#expr. Thank you again!
the #show macro already exists for this. It is helpful to be able to implement it yourself, so later you can do other likes like make one that will show the size of an Array..
For your particular variant:
Answer is Meta.quot,
macro dprint(vars...)
ex = Expr(:block)
ex.args = [:(println($(Meta.quot(v)), " = ", $v)) for v in vars]
ex
end
See with:
julia> a=2; b=3;
julia> #dprint a
a = 2
julia> #dprint a b
a = 2
b = 3
oxinabox's answer is good, but I should mention the equivalent to ,#x is $(x...) (this is the other part of your question).
For instance, consider the macro
macro _begin(); esc(:begin); end
macro #_begin()(args...)
quote
$(args...)
end |> esc
end
and invocation
#begin x=1 y=2 x*y
which (though dubiously readable) produces the expected result 2. (The #_begin macro is not part of the example; it is required however because begin is a reserved word, so one needs a macro to access the symbol directly.)
Note
julia> macroexpand(:(#begin 1 2 3))
quote # REPL[1], line 5:
1
2
3
end
I consider this more readable, personally, than pushing to the .args array.

How to simplify those tow macro when runtime type depend?

guys,
look at the code first:
(defmacro map-remove- [v w]
`(dosync
(ref-set ~v (dissoc #~v (keyword ~w)))))
(defmacro set-remove- [v w]
`(dosync
(ref-set ~v (disj #~v ~w))))
(defmacro clean- [v]
`(dosync
(ref-set ~v (empty #~v))))
They work fine now , but I want write a more general macro to combine "map-remove-" and "set-remove-" in one. according my C/Java experience I chose "case" but obviously the case can't use in macro defination cause "The test-constants are not evaluated. They must be compile-time literals", the following code won't work:
(defmacro [x]
(case (type x) ;;;;;;;;;;; This will never work!directly reach to default clause
....))
anybody has any suggestion? very appreciate.
You can use the functions map? and set? to test whether a value is a map or a set respectively.
I'm not sure I have enough perspective on what you're trying to do here though - I've substituted a macro instead of a function, because I can't see the necessity for a macro in the sample you've given.
;; Create a function to remove in a different way when given a map/set and a string
(defn remove- [v w]
(dosync
(cond
(map? #v)
(ref-set v (dissoc #v (keyword w)))
(set? #v)
(ref-set v (disj #v w)))))
;; Set up some mutable refs
(def m (ref {:a 1 :b 2}))
(def s (ref #{"a" "b" "c"}))
;; Remove from the refs
(remove- m "b") => {:a 1}
(remove- s "b") => #{"a" "c"}
On a side note - are you sure you need to use refs? I know that coming from a C/Java background, mutability is the default, but I've never actually had to use it in Clojure so far. Clojure places a lot of emphasis on immutability, and most things can be done (often very elegantly) just by using functions on immutable values.

Clojure - a let in a macro won't work

I have created a macro which creates a named dispatcher with 3 associates functions get-dispatcher, set-dispatcher and call-dispatcher to work with the dispatcher (they get a dispatching function, add one or call one). It all works just fine! However, now I want to automate the related functions names creation, thus I am putting all these internals of the macro into a let which defines that simple construction function. Note that in the code below only the get- function's name is constructed with that automation. The set- and call- ones name creation still has that manual smell.
(defmacro create-dispatcher [name]
;creates a set of dispatching functions tagged
`(do
;define dispatcher
(def ~(symbol name) ~(atom {}))
(let
[name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
; -- define getter
(defn (name-w-prefix "get")
"get-dispatcher [tag]: get a dispatcher fn by tag"
(~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
(~'[tag]
(do
(println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
; return the tagged dispatcher
( (keyword ~'tag) #~(symbol name) )))
)
; -- define caller
(defn ~(symbol (str "call-" name))
"get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
~'[tag & args]
(apply (~(symbol (str "get-" name)) ~'tag) ~'args)
)
; -- define setter
(defn ~(symbol (str "set-" name))
~'[tag fn]
"add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
(swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
)
)
; -- report
(println "created dispatcher set for '" ~(str name) "' ok!")
))
However, there is a problem. The name-w-prefix in the let statement binding causes errors. How can I fix that?
(also any advices on improvement are welcome since I am a newb and that is almost the first thing that I wrote in Clojure)
All symbols in a macro are resolved in the current namespace and expected to evaluate to a var. You could quote the name-w-prefix symbol, but that would risk colliding with symbols passed in to the macro during macro expansion. So, Clojure provides a special syntax for use in syntax-quoted forms for generating symbols - just append a # to the end of the symbol and Clojure will treat that as a quoted, auto-generated symbol. So in this case, replace occurrences of name-w-prefix with name-w-prefix# and you should be good to go.
Taking a step back and looking at what your overall goal is, I think you should move the name-w-prefix definition outside the syntax quotes and then use syntax-escape to call it. Otherwise, you'll get still more errors because defn requires a symbol, so once expanded the macro must produce a defn form that has a symbol as its second item. Something along the lines of:
(defmacro create-dispatcher [name]
(let [name-w-prefix #(symbol (str % "-" name))]
`(do
(def ~(symbol name) (atom {}))
(defn ~(name-w-prefix "get")
([] (println "no tag provided"))
([tag#] (println "called with tag" tag#))))))
Note that I've changed ~'[tag] to [tag#] in the defn body in accordance with what I was talking about above.

Getting nil instead of a return value from ScheduledFutureTask when the callable is a Clojure function

I'm trying to schedule a Clojure function using Executors.newSingleThreadScheduledExecutor(). The annoyance is that calling .get() on the resulting ScheduledFutureTask returns nil instead of the function's result.
I'm taking Mr Hickey's implementation of futures as the model.
(ns my.ns
(:import (java.util.concurrent Executors ThreadFactory TimeUnit)))
(def ^:private thread-pool-scheduler (atom nil))
(def ^:private thread-pool-counter (agent 0))
(defn- get-and-increment-thread-id []
(-> thread-pool-counter (send inc) deref))
(def ^:private thread-factory
(reify ThreadFactory
(newThread [this f]
(let [thread (Thread. f)]
(.setName thread (format "clojure-scheduled-future-thread-pool-%d"
(get-and-increment-thread-id)))
thread))))
(defn scheduled-future-call [^Callable f ^long delay ^TimeUnit unit]
(.schedule (scheduled-futures-executor) (bound-fn* f) delay unit))
(defn start-scheduled-futures-executor! []
(reset! thread-pool-scheduler
(Executors/newSingleThreadScheduledExecutor thread-factory)))
(defn scheduled-futures-executor []
(or #thread-pool-scheduler
(start-scheduled-futures-executor!)))
Everything works, and side-effects fire when appropriate (e.g. scheduling #(println "ok")).
However, calling the get() method of the resulting ScheduledFutureTask always gives me nil
(e.g. scheduling #(+ 5 5)).
I tried extending Callable explicitly, tried omitting bound-fn*, but the result is the same:
(defprotocol ISchedule
(schedule [this delay time-unit]))
(extend Callable
ISchedule
{:schedule (fn [this delay time-unit]
(.schedule (scheduled-futures-executor) this delay time-unit))})
My intuition is that the ScheduledExecutorService is choosing schedule(Runnable, long, TimeUnit) over schedule(Callable, long, TimeUnit), but shouldn't type hints fix that?
Many, many thanks for any help or tips!
My intuition is that the ScheduledExecutorService is choosing schedule(Runnable, long, TimeUnit) over schedule(Callable, long, TimeUnit),
I think you're right, only I reified Callable and it worked properly. (I also added Callable to the list of classes in the import statement).
EDIT: I also removed the call to bound-fn*
Check it out:
(defn make-callable [f] (reify Callable (call [this] (f))))
(def cat (make-callable (fn [] (println "meeow") "i am a cat")))
(def who-are-you? (scheduled-future-call cat 2 TimeUnit/SECONDS))
(println "Tell me who you are!\n\t" (.get who-are-you?))
outputs:
meeow
Tell me who you are!
i am a cat
The ^Callable hint on f does you no good, because you just call bound-fn anyway, whose result is not type-hinted. You need to hint the thing you actually pass to .schedule. More importantly, you need to hint the target object itself (the executor), as the compiler will (quite reasonably) ignore any hints on arguments if the target object is not hinted: it has to do reflection anyway if it doesn't know the type of the target!
So, hint both of those things in a let-binding1 and you should be fine. You might need to hint all the rest of the arguments as well for disambiguation, but I don't think so.
1 Note: do not hint the expressions generating them, eg ^Callable (bound-fn f). That usually works, but sometimes doesn't, in scenarios that are complicated to explain. Best to just avoid that scenario.

Calling a Clojure function with string inside swap?

The macro, transform!, as defined below seems to work for => (transform! ["foo" 1 2 3]). The purpose is to take in a list, with the first element being a string that represents a function in the namespace. Then wrapping everything into swap!.
The problem is that transform! doesn't work for => (transform! coll), where (def coll ["foo" 1 2 3]). I am getting this mystery exception:
#<UnsupportedOperationException java.lang.UnsupportedOperationException: nth not supported on this type: Symbol>
The function:
(defmacro transform!
" Takes string input and update data with corresponding command function.
"
[[f & args]] ;; note double brackets
`(swap! *image* ~(ns-resolve *ns* (symbol f)) ~#args))
I find it strange that it works for one case and not the other.
Macros work at compile-time and operate on code, not on runtime data. In the case of (transform! coll), the macro is being passed a single, unevaluated argument: the symbol coll.
You don't actually need a macro; a regular function will suffice:
(defn transform! [[f & args]]
(apply swap! *image* (resolve (symbol f)) args)))
Resolving vars at runtime could be considered a code smell, so think about whether you really need to do it.
You're passing a symbol to the macro, namely coll. It will try to pull that symbol apart according to the destructuring statement [f & args], which won't be possible of course.
You can also use (resolve symbol) instead of (ns-resolve *ns* symbol).