Composing forms using -> and ->> suddenly stops being fun when you encounter mismatch in “data-parameter” (?) position. So what do you think of thread-at macro?
looks like this:
(defmacro .>
[x & forms]
(loop [x x, forms forms]
(if forms
(let [[form & other] forms
threaded (with-meta
(map (fn [f] (if (= '% f) x f)) form)
(meta form))]
(recur threaded other))
x)))
And I don’t recommend you use the % symbol for the anaphore. It’ll clash with its use inside short lambda notation #(* % %). Normally it or <> are more common choices as anaphore.
That said, technically, any choice could have a conflict, which is why you generally want to limit the number of anaphoric macros you write, and instead offer a choice of the symbol to the user as an argument.
Another downside is that each anaphoric macro can mean you need to remember what secret symbols it uses, which is why people tend to stick to common ones like it or <>.
For your use case though, I think people do, I’ve seen a few it-> or -<> arrow macros in the wild, and I personally use the latter one. It’s nice for non nested cases, or use inside a ->>.
(defmacro <-
"Converts a ->> to a ->
(->> (range 10) (map inc) (<- (doto prn)) (reduce +))
Jason W01fe is happy to give a talk anywhere any time on
the calculus of arrow macros"
[& body]
`(-> ~(last body) ~@(butlast body)))
Generally I think that if I need to mix threading modes in a single run I’m probably doing something wrong. thread-first is for things, thread-last is for collections. Just as in languages that have method chaining, you typically want to keep the “subject” of the same ilk during a statement.
That said, I have often nested a thread-last inside a thread first when it simplifies things, particularly with the clojure.java.jdbc query/insert/update/execute/etc commands, since I “build” the query above that and process the results below it.
I find that interesting (as the maintainer of clojure.java.jdbc) since nearly all of the functions there take the db-spec first, followed by the SQL+params vector, followed by the (optional) options hash map – does that mean you are rarely using the options hash map argument?
I ask because in next.jdbc, I plan to remove most (nearly all) of the options because I’ve come to believe people aren’t using them at the per-statement level (and would most likely only want to use them at the db-spec level.
Yeah, I know you’re the maintainer, which is why I used it as an example.
Quite honestly I had forgotten about the options argument. Looking at it now, I don’t see much that I would use.
I prefer to transform column names in my own code – such that the difference between keywords :foo/bar, :foo-bar and column name foo_bar is in part something I consider to be something that should be in my code.
I’ve also taken to performing row transformation after the results come back, typically via map, but sometimes via core.async channels or whatnot.
Typically we’ll build up a query with say, honeysql in thread-first until it gets to the [querystring, …variables] stage and then thread-last that through jdbc and processing
The current option handling code – and transformation of column names to keywords – is most of the performance overhead in clojure.java.jdbc compared to bare Java code. Using reducible-query with no identifier transformation can get pretty close to you pretty close to Java performance but isn’t anywhere close to the default behavior – something I’m looking at changing in next.jdbc.