Nice, I would definitely use this! One comment/question: would it be more efficient to expand to a bunch of nested "let" statements, rather than nested function calls? I'm not sure how Clojure handles "let" under the hood, or how Hotspot inlining works here. Here's my version:
(defmacro let-> "Provide a name that will be bound to the result of the first form. For each additional form, the variable will be used in the invocation, and then rebound to the result of the form." [varname start & forms] (if (empty? forms) start `(let [~varname ~start] (let-> ~varname ~...@forms)))) In a few quick timing tests I see no real difference, so maybe it doesn't matter. -Jason On Feb 9, 2:31 pm, Mark Fredrickson <mark.m.fredrick...@gmail.com> wrote: > This inspired me to write a general purpose version: > > (defmacro let-> > "Provide a name that will be bound to the result of the first form. > For each additional form, the variable will be > used in the invocation, and then rebound to the result of the form." > [varname start & forms] > (let [fn-args `[~varname] > wrapped (map (fn [form] `(fn ~fn-args ~form)) forms)] > (reduce > (fn [acc func] `(~func ~acc)) > start > wrapped))) > > Some examples: > > Gorilla=> (let-> x (+ 1 2) (* 2 x) (+ x 1)) > 7 > Gorilla=> (let-> x [1 2 3] (map inc x) (reduce + x) (+ x 3)) > 12 > Gorilla=> (macroexpand-1 (quote (let-> x [1 2 3] (map inc x) (reduce + > x) (+ x 3)))) > ((clojure.core/fn [x] (+ x 3)) ((clojure.core/fn [x] (reduce + x)) > ((clojure.core/fn [x] (map inc x)) [1 2 3]))) > > I tried a more general version that allowed any binding form and > multiple variables in the first position, but it never seemed as clean > as the single variable version, so I reverted to the simpler case. I > also considered using function literals and % notation, but decided > that since you couldn't nest them and map/reduce/etc with function > literals are very common, using an explicitly bound var would be the > best option. > > The doc string could probably be more clear. Suggestions and > improvements welcome. > > -Mark > > On Feb 7, 2009, at 6:27 PM, MattH wrote: > > > > > > > Hi, > > I want to suggest a "pipe" macro for dealing with collection streams, > > as a one-line variation on the built in "->" macro. > > > Instead of writing a nested stream like this: > > ; "Take the first 3 elements of the odd numbers in the range 1 to > > 20" > > (take 3 (filter odd? (range 1 20))) > > > you can write this: > > ; "Take the range 1 to 20, filter the odd numbers, then take the > > first 3" > > (pipe (range 1 20) (filter odd?) (take 3)) > > > I think both forms have their place. The piped form seems useful when > > you want to emphasise the collection, or when the chain gets very > > long. It's also close to how I'd draw the chain on a whiteboard or > > describe it in speech, and helps me to reason about the chain from > > left to right in small "chunks". > > > The difference with "->" is this: > > (-> x (f a b)) => (f x a b) > > (pipe x (f a b)) => (f a b x) > > > The definition is the equivalent of the "->" macro (defined in > > core.clj, line 984), swapping "~@(rest form) ~x" to "~x ~@(rest > > form)": > > > (defmacro pipe > > "Threads the expr through the forms. Inserts x as the > > last item in the first form, making a list of it if it is not a > > list already. If there are more forms, inserts the first form as the > > last item in second form, etc." > > ([x form] (if (seq? form) > > `(~(first form) ~@(rest form) ~x) > > (list form x))) > > ([x form & more] `(pipe (pipe ~x ~form) ~...@more))) > > > I've seen pipe written as a function with the use of reader macros, or > > as macros built from scratch. I'm putting this one forward because the > > tiny change in intention from "->" is reflected as a tiny delta in the > > code. > > > Cheers, > > Matt > > - Mark Fredrickson > mark.m.fredrick...@gmail.comhttp://www.markmfredrickson.com --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---