Try + finally question

853 views
Skip to first unread message

ka

unread,
Apr 21, 2010, 7:46:57 AM4/21/10
to Clojure
Hi,

I'm using an API to connect to a server which does not provide
constructors, just static methods to get & close connections.

How can I do something like ...

(try
(let [conn (API/getConnection ..)]
(....))
(catch ..)
(finally (if conn (API/closeConnection conn))))

Problem is that conn is unavailable in the finally context.

Is there any easy workaround which doesn't involve defing a global
conn.

- Thanks

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Alex Osborne

unread,
Apr 21, 2010, 7:58:10 AM4/21/10
to clo...@googlegroups.com
ka <sanc...@gmail.com> writes:

> How can I do something like ...
>
> (try
> (let [conn (API/getConnection ..)]
> (....))
> (catch ..)
> (finally (if conn (API/closeConnection conn))))
>
> Problem is that conn is unavailable in the finally context.

Why not just have two try blocks?

(try
(let [conn (API/getConnection ..)]
(try
(....)
(finally (API/closeConnection conn))))
(catch ...))

For these sorts of things I usually try to use with-open, or if
with-open is not applicable do my own similar macro:

(defmacro with-api-connection [conn-sym arg1 arg2 & body]
`(let [~conn-sym (API/getConnection ~arg1 ~arg2)]
(try
~@body
(finally (API/closeConnection ~conn-sym)))))

So then you never forget to close the connection:

(try
(with-api-connection conn ...
(... do something with conn ...))
(catch ...))

ka

unread,
Apr 21, 2010, 9:06:40 AM4/21/10
to Clojure
Thanks for the reply Alex,

I thought of 2 try blocks, but seems over engineering doesn't it ? I
actually need to open 2 connections simultaneously, which would amount
to 3 nested try blocks. 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)))

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)))))

Also coming back to the original requirement, wanted to know what are
the problems with providing support (somehow) for something like -

(try
(let [conn1 (API1/getConnection ..)
conn2 (API2/getConnection ..)]
(....))
(catch ..)
(finally
(if conn1 (API1/closeConnection conn1))
(if conn2 (API2/closeConnection conn2))))


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?

- Thanks!

On Apr 21, 4:58 pm, Alex Osborne <a...@meshy.org> wrote:

Mark J. Reed

unread,
Apr 21, 2010, 9:26:48 AM4/21/10
to clo...@googlegroups.com
On Wed, Apr 21, 2010 at 9:06 AM, ka <sanc...@gmail.com> wrote:
Connection1 conn1 = null;
Connection2 conn2 = null;
try {
 conn1 = API1.getConnection ..;
 conn2 = API2.getConnection ..;
 ...
}

You can't do that without using atoms or something, because you're assigning to conn1 and conn2 twice.  That's not legal in Clojure because what "let" gives you aren't variables, but local constants.   

One solution would be to add a new construct to the language:

(try-let [bindings] (body) (catch...) (finally...))

where any exception during evaluation of the [bindings] would be caught just as if it happened inside the body, and of course the bound variables would be visible to both the catch blocks and the finally block.   

It could be implemented as a macro that duplicated the catch/finally blocks within and outside a let...

-- 
Mark J. Reed <mark...@gmail.com>

Laurent PETIT

unread,
Apr 21, 2010, 9:37:21 AM4/21/10
to clo...@googlegroups.com
Hi,

Something I don't understand: if the call to (API/getConnection ...)
fails, there is nothing to close, right ?

So for the problem of ensuring that any open connection is always
closed, the following pattern seems enough:

(try
(let [conn (API/getConnection ..)]
XXX)
(finally (API/closeConnection conn)))

In XXX you can do your business job, maybe catching exceptions, etc.,
but that is not the same intent as ensuring pairs of open/close calls
in any case.

Smart java frameworks such as SpringFramework do the exact same thing
by using Interface callbacks encapsulating the XXX "real interesting
code".

The less verbose way of doing this is clojure's with-open macro.
Though you would have, as Alex pointed out, to create your own variant
of with-open to accomodate your particular API for closing the
connection.

Or, if you use master, you could write the protocol-aware version of
with-open :-)


2010/4/21 ka <sanc...@gmail.com>:

Laurent PETIT

unread,
Apr 21, 2010, 9:40:52 AM4/21/10
to clo...@googlegroups.com
Oh, also see the source code for with-open :
http://github.com/richhickey/clojure/blob/master/src/clj/clojure/core.clj#L2542

and also note that it allows you to declare bindings for opening
connections in sequence, and will take care of closing them in the
reverse order.

And to answer your question about writing 2 macros if you have
different APIs, no no :

* either use protocols if you work on the master branch
* either use multimethods

that is, use polymorphism :-)

2010/4/21 Laurent PETIT <lauren...@gmail.com>:

Mark J. Reed

unread,
Apr 21, 2010, 9:50:08 AM4/21/10
to clo...@googlegroups.com
On Wed, Apr 21, 2010 at 9:37 AM, Laurent PETIT <lauren...@gmail.com> wrote:
Hi,

Something I don't understand: if the call to (API/getConnection ...)
fails, there is nothing to close, right ?

So for the problem of ensuring that any open connection is always
closed, the following pattern seems enough:

(try
 (let [conn (API/getConnection ..)]
    XXX)
 (finally (API/closeConnection conn)))

That would be great, but it doesn't work, because "conn" doesn't exist any more by the time you get to the finally block. That's the problem being addressed in this thread.

So you can do this:

(let [conn (API/getConnection ...)]
   (try 
      XXX
   (finally (API/closeConnection conn))))

But then any exception thrown by API/getConnection is not caught. 

So you have to do this:

(try 
    (let [conn (API/getConnection ...)]
      (try
        XXX
      (finally (API/closeConnection conn))))
   (catch ...))

But that still won't work if the catch block also needs access to "conn".  So you wind up with something like this:

(try 
    (let [conn (API/getConnection ...)]
      (try
        XXX
      (catch ExceptionAfterConnect x (do-something-with conn))
      (finally (API/closeConnection conn))))
   (catch .ExceptionDuringConnect x (do-something-without-conn))))

-- 
Mark J. Reed <mark...@gmail.com>

Laurent PETIT

unread,
Apr 21, 2010, 9:58:34 AM4/21/10
to clo...@googlegroups.com
2010/4/21 Mark J. Reed <mark...@gmail.com>:
> On Wed, Apr 21, 2010 at 9:37 AM, Laurent PETIT <lauren...@gmail.com>
> wrote:
>>
>> Hi,
>>
>> Something I don't understand: if the call to (API/getConnection ...)
>> fails, there is nothing to close, right ?
>>
>> So for the problem of ensuring that any open connection is always
>> closed, the following pattern seems enough:
>>
>> (try
>>  (let [conn (API/getConnection ..)]
>>     XXX)
>>  (finally (API/closeConnection conn)))

Of course, My code was wrong, and I meant


(let [conn (API/getConnection ..)]
(try
XXX
(finally (API/closeConnection conn)))

And really, catching exceptions thrown by API/getConnection is not in
the problem domain of ensuring balanced pairs of opened/closed
connections, e.g. should be outside the abstraction you put in place
for handling this (e.g. with-open).

Ensuring that opened connections are closed is a resource (I/O)
management problem.
Handling the case where you were not able to open a connection is an
application problem. You might place the try/catch right around the
let, have a global handling very high in the call stack, or even have
no try/catch at all.

HTH,

--
Laurent

Tassilo Horn

unread,
Apr 21, 2010, 10:07:20 AM4/21/10
to clo...@googlegroups.com
On Wednesday 21 April 2010 15:06:40 ka wrote:

Hi!

> The macro solution looks good. But with 2 different APIs for 2
> connections, I would need to write 2 macros right?

No, not really. You could also make the symbols for the API class an
additional arg to the macro, like:

(defmacro with-api-connection [conn-sym api arg1 arg2 & body]
`(let [~conn-sym (. ~api getConnection ~arg1 ~arg2)]
(try
~@body
(finally (. ~api closeConnection ~conn-sym)))))

but then the macre application would be longer.

(with-api-connection [c API1 "localhost" 12345]
(with-api-connection [c2 API2 "141.55.22.1" 13321]
(do-stuff-with c c2))
(do-stuff-with c))

Bye,
Tassilo

Alex Osborne

unread,
Apr 21, 2010, 10:05:06 AM4/21/10
to clo...@googlegroups.com
ka <sanc...@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

ka

unread,
Apr 21, 2010, 10:43:12 AM4/21/10
to Clojure
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:

Laurent PETIT

unread,
Apr 21, 2010, 10:53:32 AM4/21/10
to clo...@googlegroups.com
Hi,

I don't know for sure, but it seems to me that perl 6 way of "writing"
exception handling is just a syntactic difference.

For an interesting shift in the possibilities of exception
handling/recovery, have a look at how Common Lisp does this with
conditions/restart :
http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html

Which has been ported to clojure by chouser:
http://richhickey.github.com/clojure-contrib/error-kit-api.html

In a nutshell, with this you can simulate handlers placed higher in
the call stack, but those handlers could compute a "recovery value"
that would replace the value returned by the innermost from which the
exception originated (though this is totally off topic with your
original post :-) ).

2010/4/21 ka <sanc...@gmail.com>:

Mark J. Reed

unread,
Apr 21, 2010, 11:04:28 AM4/21/10
to clo...@googlegroups.com
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.
--
Mark J. Reed <mark...@gmail.com>

ka

unread,
Apr 23, 2010, 8:48:26 AM4/23/10
to Clojure
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
> > > clojure+u...@googlegroups.com<clojure%2Bunsu...@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 clo...@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+u...@googlegroups.com<clojure%2Bunsu...@googlegroups.com>
> > For more options, visit this group at
> >http://groups.google.com/group/clojure?hl=en
>
> --
> Mark J. Reed <markjr...@gmail.com>

Armando Blancas

unread,
Apr 23, 2010, 11:10:04 AM4/23/10
to Clojure
> Is there any easy workaround which doesn't involve defing a global
> conn.

To do all in a single place you'll have to mimic the Java idiom.

(let [conn (atom nil)]
(try
(reset! conn (API/getConnection ...) ...
(catch ...)
(finally (if @conn (API/closeConnection @conn)))))

As it's been explained, a better way would be a variant of with-open,
maybe within a try form if you want to handle open errors locally.

ataggart

unread,
Apr 23, 2010, 7:07:08 PM4/23/10
to Clojure
I wrote this, which seems to solve the problem being discussed:

http://gist.github.com/377278

Thoughts?

ka

unread,
Apr 29, 2010, 4:10:14 AM4/29/10
to Clojure
Hi ppl,

Above I wrote a macro with-open-flexi! ... which I'm planning to use
in my app's API . .. please let me know if there are any bugs /
gotchas / improvements etc...

I didn't get any responses, so does it means there is something so
obviously wrong that you can't even begin where to start :) (I doubt
so) or does it looks OK ..?

(See the above post for detailed comments & usage)

(defmacro with-open-flexi!
[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))))))

- Thanks

On Apr 24, 4:07 am, ataggart <alex.tagg...@gmail.com> wrote:
> I wrote this, which seems to solve the problem being discussed:
>
> http://gist.github.com/377278
>
> Thoughts?
>
> On Apr 23, 8:10 am, Armando Blancas <armando_blan...@yahoo.com> wrote:
>
>
>
> > > Is there any easy workaround which doesn't involve defing a global
> > > conn.
>
> > To do all in a single place you'll have to mimic the Java idiom.
>
> > (let [conn (atom nil)]
> >   (try
> >     (reset! conn (API/getConnection ...) ...
> >     (catch ...)
> >     (finally(if @conn (API/closeConnection @conn)))))
>
> > As it's been explained, a better way would be a variant of with-open,
> > maybe within atryform if you want to handle open errors locally.

Alex Osborne

unread,
Apr 29, 2010, 5:04:25 AM4/29/10
to clo...@googlegroups.com
ka <sanc...@gmail.com> writes:

> Above I wrote a macro with-open-flexi! ... which I'm planning to use
> in my app's API . .. please let me know if there are any bugs /
> gotchas / improvements etc...
>
> I didn't get any responses, so does it means there is something so
> obviously wrong that you can't even begin where to start :) (I doubt
> so) or does it looks OK ..?

It looks okay to me, nothing obviously stands out as wrong with it. :-)

Laurent PETIT

unread,
Apr 29, 2010, 5:24:37 AM4/29/10
to clo...@googlegroups.com
Maybe juste the name ?

wouldn't with-open-close be a better reminder of how the bindings are
constructed ?

2010/4/29 Alex Osborne <a...@meshy.org>:

ka

unread,
May 1, 2010, 5:01:01 AM5/1/10
to Clojure
Hmm .. makes sense .. my thinking was its just a more flexible with-
open ... but from user's pov with-open-close is better...

Thanx
> For more options, visit this group athttp://groups.google.com/group/clojure?hl=en
Reply all
Reply to author
Forward
0 new messages