Exceptions as flow control

275 views
Skip to first unread message

jim

unread,
May 2, 2008, 11:39:03 AM5/2/08
to Clojure
To write reliable code, you have to account for every possible result
of calling a function. You can make software work without doing that,
but then there's always the possibility of some rare condition
happening that causes the program to crash.

Dan Wienreb's post about conditions, exceptions and errors explains it
far better.

http://dlweinreb.wordpress.com/2008/03/24/what-conditions-exceptions-are-really-about/

As he states, handling special return values to account for unusual
results leads to code bloat. So my question for the group is; Is it
valid to use exceptions as a form of flow control?

I'm leaning towards yes. When I first started learning Python, I saw
some code that threw an exception to break out of a loop. That may be
a little extreme, but it did allow the idea behind the algorithm to be
expressed very succinctly. After reading Wienreb's post, I'm thinking
that it is acceptable to throw an exception in a function whenever a
usual result isn't returned. The problem that leads to is the
proliferation of exception class hierarchies. And currently in
Clojure we can't even create new exception classes without dropping
into Java.

Blue sky thinking: I'd like to see the ability to define new exception
classes in Clojure and then tools to specify the contract a function
will adhere to, including the values it may return and exceptions it
may throw.

Thoughts, criticisms?
jim

Randall R Schulz

unread,
May 2, 2008, 11:52:30 AM5/2/08
to clo...@googlegroups.com
On Friday 02 May 2008 08:39, jim wrote:
> ... So my question for the group is; Is it

> valid to use exceptions as a form of flow control?

Well, this is going to spark a lot of debate. Or it would on a Java
programmer's list, anyway.

The one thing you'll need to do at a minimum if you're going to use a
lot of exception throwing for non-local, non-exceptional control flow
is use the trick of overriding the "fillInStackTrace()" method to be a
no-op. This method accounts for the vast majority of the cost of
new-ing a Throwable.


> ...
>
> Thoughts, criticisms?

I've said it before and I'll say it again: I'm not paid to think.


> jim


Randall Schulz

jim

unread,
May 2, 2008, 12:02:19 PM5/2/08
to Clojure
Interesting. Not having much Java experience, that didn't occur to
me.

You'd definitely want to do that for non-error type conditions, I
would think.

jim

Randall R Schulz wrote:
>
> The one thing you'll need to do at a minimum if you're going to use a
> lot of exception throwing for non-local, non-exceptional control flow
> is use the trick of overriding the "fillInStackTrace()" method to be a
> no-op. This method accounts for the vast majority of the cost of
> new-ing a Throwable.
>
> Randall Schulz

Rich Hickey

unread,
May 2, 2008, 4:40:44 PM5/2/08
to Clojure


On May 2, 11:39 am, jim <jim.d...@gmail.com> wrote:
> To write reliable code, you have to account for every possible result
> of calling a function. You can make software work without doing that,
> but then there's always the possibility of some rare condition
> happening that causes the program to crash.
>
> Dan Wienreb's post about conditions, exceptions and errors explains it
> far better.
>
> http://dlweinreb.wordpress.com/2008/03/24/what-conditions-exceptions-...
>
> As he states, handling special return values to account for unusual
> results leads to code bloat. So my question for the group is; Is it
> valid to use exceptions as a form of flow control?
>

No.

> I'm leaning towards yes. When I first started learning Python, I saw
> some code that threw an exception to break out of a loop. That may be
> a little extreme, but it did allow the idea behind the algorithm to be
> expressed very succinctly. After reading Wienreb's post, I'm thinking
> that it is acceptable to throw an exception in a function whenever a
> usual result isn't returned. The problem that leads to is the
> proliferation of exception class hierarchies. And currently in
> Clojure we can't even create new exception classes without dropping
> into Java.
>
> Blue sky thinking: I'd like to see the ability to define new exception
> classes in Clojure and then tools to specify the contract a function
> will adhere to, including the values it may return and exceptions it
> may throw.
>
> Thoughts, criticisms?

You have to be careful drawing parallels between Java exceptions and
CL conditions. And don't neglect David Moon's comment on the
distinction between can continue and can't.

In Clojure, reserve exceptions for 'can't'.

You can use dynamic binding of functions to emulate the resumable part
of conditions.

Rich

jim

unread,
May 5, 2008, 6:27:56 PM5/5/08
to Clojure
After thinking about the dynamic binding of functions, it looks like I
can get what I want that way. Which is something like the CL
conditions. With that, I agree that throwing exceptions and errors
should be done only in a "can't continue" situation.

Thanks,
Jim

On May 2, 3:40 pm, Rich Hickey <richhic...@gmail.com> wrote:
> On May 2, 11:39 am, jim <jim.d...@gmail.com> wrote:
>
> > So my question for the group is; Is it
> > valid to use exceptions as a form of flow control?
>
> No.
>
>

Scott Parish

unread,
May 6, 2008, 10:42:35 PM5/6/08
to clo...@googlegroups.com
I was also recently fighting the urge to use conditionals in clojure.
I usually use such in cases where i have several loops with rather non-
shallow function stacks where i don't want to have to train every loop
and every function about what potential exit conditions look like. Its
not clear right off how the "dynamic binding of functions" help with
this. What am i missing? (my initial guess would be recursion, but
since there is no tail-call optimization, that can't be it)

Thanks
sRp

Rich Hickey

unread,
May 6, 2008, 11:47:28 PM5/6/08
to Clojure


On May 6, 7:42 pm, Scott Parish <s...@srparish.net> wrote:
> I was also recently fighting the urge to use conditionals in clojure.
> I usually use such in cases where i have several loops with rather non-
> shallow function stacks where i don't want to have to train every loop
> and every function about what potential exit conditions look like. Its
> not clear right off how the "dynamic binding of functions" help with
> this. What am i missing? (my initial guess would be recursion, but
> since there is no tail-call optimization, that can't be it)
>

I'm not quite sure what you means by conditionals above. Are you
talking about Common Lisp-style conditions?

Rich

Scott Parish

unread,
May 7, 2008, 12:09:12 AM5/7/08
to clo...@googlegroups.com
Yes, sorry i meant "conditions" not "conditionals". The ability to
restart is nice, but what i miss more is just the ability to
"throw" (non-error) values out of semi-deeply nested function calls,
rather then having to handle return values all the way back down the
stack.

sRp

Rich Hickey

unread,
May 7, 2008, 10:12:55 AM5/7/08
to Clojure


On May 6, 9:09 pm, Scott Parish <s...@srparish.net> wrote:
> Yes, sorry i meant "conditions" not "conditionals". The ability to
> restart is nice, but what i miss more is just the ability to
> "throw" (non-error) values out of semi-deeply nested function calls,
> rather then having to handle return values all the way back down the
> stack.
>

Dynamic vars are the way, other than exceptions, to communicate across
the stack without passing/returning. You can use them for non-normal
but not necessarily throw-worthy anomalies in several ways.

One is to define a function that will be called by a nested function
when it is in trouble, i.e. data-getter is speced to call malformed-
input when it has trouble, passing the input in question, and promises
to use the return value if non-nil or ignore it if nil. The default
version of malformed-input throws an exception.

Then someone higher up, who knows the malformed input policy, can bind
malformed-input to a function that might be able to fix some bad
inputs, or prompt the user, log it or whatever.

A second way is to use some data-bearing var to hold any anomalous
values seen in the nested computation.

I'm sure there are other recipes.

I think that between exceptions and dynamic vars, Clojure doesn't need
CL-style conditions.

Rich

jim

unread,
May 7, 2008, 10:49:11 AM5/7/08
to Clojure
That's what I discovered. By rebinding some functions, I'm able to
basically do what I wanted to accomplish with conditions. I also
established a convention for myself that any functions that can be
rebound to handle conditions are gathered in a list that I assign to
the :handlers keyword in the meta-data of the function. And the
purpose of those functions documented in the doc string. I suspect
with some macro magic you could implement most of the capabilities of
CL's conditions.

Another thing I did using the rebinding of function variables was to
rebind the definitions of ref-set and deref before doing a dosync
block. In the new functions, I save the ref var into a list before
calling the old definition. Then, at the end of the dosync block, I
have a list of all ref's changed or derefed in that block.

There are a lot of implications of dynamic binding of variables that
aren't immediately apparent.

jim

Scott R Parish

unread,
May 7, 2008, 6:21:38 PM5/7/08
to clo...@googlegroups.com
----- Original Message -----
Subject: Re: Exceptions as flow control
Date: Wed, May 7, 2008 7:12
From: "Rich Hickey" <richh...@gmail.com>

>
>
> On May 6, 9:09 pm, Scott Parish <s...@srparish.net> wrote:
> > Yes, sorry i meant "conditions" not "conditionals". The ability to
> > restart is nice, but what i miss more is just the ability to
> > "throw" (non-error) values out of semi-deeply nested function calls,
> > rather then having to handle return values all the way back down the
> > stack.
> >
>

> A second way is to use some data-bearing var to hold any anomalous
> values seen in the nested computation.

Ah, i think this answers my question. I can have a var that i bind the
result data to, and then throw an exception indicating successful
completion of said task, catch such exception, and retrieve the value out
of the dynamic var.

Now if it were easier to define new types of exception subclasses in
clojure. <g>

sRp

Rich Hickey

unread,
May 10, 2008, 12:55:10 AM5/10/08
to Clojure
I wasn't advocating this (exceptions on success paths). This is just
avoiding normal structured flow of control. There is a reason there
are no break/continue/returns in Clojure. You can instead set a
dynamically bound var and return normally.

Rich
Reply all
Reply to author
Forward
0 new messages