Hi all, I've written my first macro: with-open-flexi!
(defmacro with-open-flexi! " Just like with-open except it takes closing expression instead of assuming it to be .close Macro definition : (with-open-flexi! [] body) expands to - (do body) (with-open-flexi! [obj1 init1 close1 more] body)) expands to (let [obj1 init1] (try (with-open-flexi! [more] body) (finally close1))) Note: It is your responsibility to ensure - 1. Count of elements in the bindings is a multiple of 3 2. 0th, 3rd, 6th, 9th ... index of binding is a symbol example usage - (with-open-flexi! [conn1 (API1/Conn args) (API1/Disconn conn1 args) conn2 (API2. args) (API2/Disconn args conn2) conn3 (API3. args) (.disconn conn3)] (... use conn1, conn2, conn3 ...) 42) " [bindings & body] (if (= 0 (count bindings)) `(do ~...@body) (let [[o i c & more] bindings] `(let [~o ~i] (try (with-open-flexi! [...@more] ~...@body) (finally ~c)))))) Tested it : => (with-open-flexi! [o1 (println "i1") (println o1 "c1") o2 (println "i2") (println o2 "c2")] (println "body1") (println "body2") 42) i1 i2 body1 body2 nil c2 nil c1 42 Please let me know if there are any bugs / gotchas / improvements etc. - Thanks On Apr 21, 8:04 pm, "Mark J. Reed" <markjr...@gmail.com> wrote: > The main thing about Perl6 in this case is that the catch/finally blocks are > inside the same scope as the try. But that's true in Clojure as well! > The difference is that Clojure's try is not itself a lexical binding scope; > you have to wrap one around it or within it via let. That's why I thought a > combination of try and let would be useful, if only as syntactic sugar. > > > > On Wed, Apr 21, 2010 at 10:43 AM, ka <sancha...@gmail.com> wrote: > > Thanks all for replies. > > > Laurent, Alex you guys are right, the problem is only with aesthetics > > of nesting / boilerplate. The nesting implementation semantically > > expresses exactly what is required. > > > The with-cleanup macro seems really neat. Guess I'll learn macros > > first and try to implement one. > > > One more interesting perspective to exceptional handling is the way > > Perl 6 is doing it - > >http://feather.perl6.nl/syn/S04.html#Exception_handlers > > > See this - > > > { > > my $s = ''; > > die 3; > > CATCH { > > when 1 {$s ~= 'a';} > > when 2 {$s ~= 'b';} > > when 3 {$s ~= 'c';} > > when 4 {$s ~= 'd';} > > default {$s ~= 'z';} > > } > > > is $s, 'c', 'Caught number'; > > }; > > > Thanks! > > > On Apr 21, 7:05 pm, Alex Osborne <a...@meshy.org> wrote: > > > ka <sancha...@gmail.com> writes: > > > > The whole code gets cluttered with all these try finally (and one > > > > catch) statements. > > > > > (try > > > > (let [conn1 (API1/getConnection ..)] > > > > (try > > > > (let [conn2 (API2/getConnection ..)] > > > > (try > > > > ( ........... Do something with conn1 conn2 ............) > > > > (finally > > > > (API2/closeConnection conn2)))) > > > > (finally > > > > (API1/closeConnection conn1)))) > > > > (catch Exception ex (.printStackTrace ex))) > > > > I guess the main difference in this compared to your java example is the > > > levels of nesting. This may look messy but it's semantically exactly > > > what you're trying to express. > > > > > The macro solution looks good. But with 2 different APIs for 2 > > > > connections, I would need to write 2 macros right? > > > > > (defmacro with-api1-connection [conn-sym arg1 arg2 & body] > > > > `(let [~conn-sym (API1/getConnection ~arg1 ~arg2)] > > > > (try > > > > ~...@body > > > > (finally (API1/closeConnection ~conn-sym))))) > > > > > (defmacro with-api2-connection [conn-sym arg1 arg2 arg3 & body] > > > > `(let [~conn-sym (API2/getConnection ~arg1 ~arg2 ~arg3)] > > > > (try > > > > ~...@body > > > > (finally (API2/closeConnection ~conn-sym))))) > > > > You could make things more general: > > > > (with-cleanup [conn1 (API1/getConnection ...) API1/closeConnection > > > conn2 (API2/openConnection ...) #(.disconnect %)] > > > ...) > > > > I'll leave implementation as an exercise, it's not much more complicated > > > than the previous ones, the main trick would just be to make the macro > > > recursive, have it expand into: > > > > (let [conn1 (API1/getConnection ...)] > > > (try > > > (with-cleanup [conn2 (API2/openConnection ...) #(.disconnect %)] > > > ...) > > > (finally > > > (API1/closeConnection conn1)))) > > > > I'd probably start with a signature like this: > > > > (defmacro with-cleanup [[sym create cleanup & more] & body] > > > ...) > > > > Take a look at the source for with-open if you get stuck. > > > > > Coming from Java, this would be implemented as - > > > > > Connection1 conn1 = null; > > > > Connection2 conn2 = null; > > > > try { > > > > conn1 = API1.getConnection ..; > > > > conn2 = API2.getConnection ..; > > > > ... > > > > } > > > > catch (){} > > > > finally { > > > > if (conn1 != null) > > > > API1.closeConnection(conn1); > > > > if (conn2 != null) > > > > API2.closeConnection(conn2); > > > > } > > > > > I agree that this code doesn't look good from a purist pov, but any > > > > issues besides that? > > > > The problem here is that this breaks lexical scope, conn1 and > > > conn2 aren't defined outside their let block. The Java example dodges > > > this with mutation. Python/Ruby/JavaScript etc dodge it by having > > > special scoping rules: variables are scoped to functions rather than the > > > enclosing block. > > > > Clojure's opinion, as I understand it, is that it's not worthwhile > > > introducing mutation or special scoping rules simply to avoid some > > > nesting, when we have perfectly good tools (macros) for doing purely > > > syntactic transformations and removing boilerplate. > > > > There's nothing semantically wrong with nesting, it's just harder > > > to read. The Clojure idiom for reducing nesting is usually to use a > > > macro like ->, ->> or with-open to flatten it. In this case those > > > aren't applicable, so I suggest defining your own. > > > > I'm not sure I phrased that clearly, please let me know if I'm not > > > making sense. :-) > > > > Alex > > > > -- > > > 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<clojure%2bunsubscr...@googlegroups.com> > > > For more options, visit this group athttp:// > > groups.google.com/group/clojure?hl=en > > > -- > > 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<clojure%2bunsubscr...@googlegroups.com> > > For more options, visit this group at > >http://groups.google.com/group/clojure?hl=en > > -- > Mark J. Reed <markjr...@gmail.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 > 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 > athttp://groups.google.com/group/clojure?hl=en -- 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