Using exceptions for early exit

R

Roedy Green

Having spent the better part of two days recently rewriting a deeply recursive
algorithm into a pure (no local variables) iterative formulation, and /NEVER/
wanting to do so again, I cannot but feel that this poor advice...

Brings back memories. Very early in my career I converted a recursive
Quicksort into non-recursive FORTRAN. What a nightmare. Recursion
really simplifies some algorithms. I could not believe how much
Fortran it took to do the same thing as a few lines of Journal of the
ACM Algol.
 
R

ricky.clarkson

Roedy,

Yes, the corner case of when you actually need lots of conversions, and
can't afford to write a separate method to wrap up the custom version
of Integer.parseInt I gave, to again throw an exception, would possibly
invalidate my suggestion.

Here is how I see the code before your suggestion:

final class LessExceptions
{
public static IntegerParseResult parseInt(final String string)
{
try
{
final int result=Integer.parseInt(string);
return new PositiveIntegerParseResult(result);
}
catch (final NumberFormatException exception)
{
return new NegativeIntegerParseResult();
}
}
}

interface IntegerParseResult
{
boolean hasValue();
int getValue();
}

final class PositiveIntegerParseResult implements IntegerParseResult
{
private final int value;

public IntegerParseResult(final int value)
{
this.value=value;
}

public boolean hasValue()
{
return true;
}

public int getValue()
{
return value;
}
}

final class NegativeIntegerParseResult implements IntegerParseResult
{
public NegativeIntegerParseResult()
{
}

public boolean hasValue()
{
return false;
}

public int getValue()
{
throw new IllegalStateException();
}
}

Then the client programmer would do:

final IntegerParseResult result=LessExceptions.parseInt("5556");

if (result.hasValue())
someMethod(result.getValue());
else
someErrorHandling();

Now the requirement is to be able to handle lots of numbers. Suppose
it's a String[]:

final String[] inputs={"5556","blah","123","566"};

for (final String input: inputs)
{
final IntegerParseResult result=LessExceptions.parseInt("5556");

if (result.hasValue())
someMethod(result.getValue());
else
someErrorHandling();
}

I can't see an issue with this. If you want to lazily load the
Strings, you could even use a collection that lazily loads, and iterate
over this, and break the loop early if you want.

Another option is to add a method to LessExceptions:

public static IntegerParseResult[] parseInts(final String... strings)
{
final IntegerParseResult[] results=new
IntegerParseResult[strings.length];

for (final int a=0;a<strings.length;a++)
results[a]=parseInt(strings[a]);
}

Of course, I wouldn't be surprised if I've completely misunderstood
your point.
 
C

Chris Uppal


I take it you are replying to me. The threading seems to be screwed up and you
haven't actually quoted anything directly.

Just because changing a recursive implementation into an iterative
implementation is awkward, doesn't mean that the iterative version is
wrong, or that it is bad advice to suggest to do so.

It does if any of the following apply:

1) the non-recursive expression of the algorithm is significantly
harder to read/understand/maintain than the recursive form.

2) the time taken to remove the recursion is greater than is
justified by removing the exception hack.

3) a recursive form, but without the exception hack, would be
acceptably clear.

My point is that it seems likely (to me) that one or another of the above would
apply.

I've never heard of this 'pure iterative' concept before, so I'm unsure
why you would aim to have no local variables. I've probably
misunderstood.

My fault. "pure iteration" isn't a recognised technical term (it would be nice
though...). I was converting a highly recursive search algorithm to a form
where it could be used to iterate over the solutions with an external iterator
like a java.util.Iterator. That meant that all the state used by the algorithm
had to be stored in the iteration object; e.g. I couldn't use local variables
in a for-loop, which made it a little bit trickier[*].

-- chris


[*] Though what really threw me was that the recursive form used 2 looping
variables (plus the recusion variables), but the iterative form only used
one -- and not the same one, at that...
 
C

Chris Uppal

Roedy Green wrote:

[me:]
Brings back memories. Very early in my career I converted a recursive
Quicksort into non-recursive FORTRAN. What a nightmare.

I was praying for Gosling to descend from on high and bless us with
Sather-style co-routines/iterators. Didn't happen...[*]

Recursion
really simplifies some algorithms. I could not believe how much
Fortran it took to do the same thing as a few lines of Journal of the
ACM Algol.

According to Hoare's ACM award speach, "The Emperor's Old Clothes", he
orginally devised Quicksort in a non-recursive form (which made it
tricky to explain) It was only when he /subsequently/ learned about
recursion (at course on Algol 60 tutored by Naur, Dijkstra, and Landin
!) that he found the elegant presentation.

-- chris

([*] Wouldn't have helped much anyway 'cos I wasn't working in Java,
but why spoil the story ;-)
 
R

ricky.clarkson

A trivial iterative thing I wrote some time ago was a
BreadthFirstIterator and a DepthFirstIterator for a Tree. While depth
first searching is usually implemented recursively (and could have been
for my use too, via a passed-in Runnable), I found the resulting
iterative design more client-programmer friendly.

I wouldn't go so far as to say recursion was an antipattern, or even a
code smell, but I do find it easier to *use*, if not *write*, code that
works iteratively. Both approaches are just as flexible, but iteration
*looks* more flexible.

There isn't enough information from the original poster, so I wouldn't
like to speculate, but in most naturally-recursive (whatever that is!)
operations, it seems against the grain to want to bail out early. My
speculation (I said I wouldn't like to do that!) is that the algorithm
is probably better written iteratively.

In software development, time taken to implement a change isn't
normally as important as the effects of that change on the simplicity
of the design. That is; DoTheSimplestThingThatCouldPossiblyWork[1]
means the simplest, not the fastest.

[1] http://c2.com/cgi/wiki?DoTheSimplestThingThatCouldPossiblyWork
 
O

Oliver Wong

Roedy,

Yes, the corner case of when you actually need lots of conversions, and
can't afford to write a separate method to wrap up the custom version
of Integer.parseInt I gave, to again throw an exception, would possibly
invalidate my suggestion.

Here is how I see the code before your suggestion:

final class LessExceptions
{
public static IntegerParseResult parseInt(final String string)
{
try
{
final int result=Integer.parseInt(string);
return new PositiveIntegerParseResult(result);
}
catch (final NumberFormatException exception)
{
return new NegativeIntegerParseResult();
}
}
}

interface IntegerParseResult
{
boolean hasValue();
int getValue();
}

final class PositiveIntegerParseResult implements IntegerParseResult
{
private final int value;

public IntegerParseResult(final int value)
{
this.value=value;
}

public boolean hasValue()
{
return true;
}

public int getValue()
{
return value;
}
}

final class NegativeIntegerParseResult implements IntegerParseResult
{
public NegativeIntegerParseResult()
{
}

public boolean hasValue()
{
return false;
}

public int getValue()
{
throw new IllegalStateException();
}
}

Then the client programmer would do:

final IntegerParseResult result=LessExceptions.parseInt("5556");

if (result.hasValue())
someMethod(result.getValue());
else
someErrorHandling();

Now the requirement is to be able to handle lots of numbers. Suppose
it's a String[]:

final String[] inputs={"5556","blah","123","566"};

for (final String input: inputs)
{
final IntegerParseResult result=LessExceptions.parseInt("5556");

if (result.hasValue())
someMethod(result.getValue());
else
someErrorHandling();
}

I can't see an issue with this. If you want to lazily load the
Strings, you could even use a collection that lazily loads, and iterate
over this, and break the loop early if you want.

Another option is to add a method to LessExceptions:

public static IntegerParseResult[] parseInts(final String... strings)
{
final IntegerParseResult[] results=new
IntegerParseResult[strings.length];

for (final int a=0;a<strings.length;a++)
results[a]=parseInt(strings[a]);
}

Of course, I wouldn't be surprised if I've completely misunderstood
your point.

Personally, I don't like the API you've exposed. For one thing, it's
impossible to get a negative parse value, despite the existence of a
"NegativeIntegerParseResult" class. For another thing, having an instance of
a class doesn't imply that you actually have a legal value, because of the
"hasValue()" boolean. That means that every method that takes in an instance
of IntegerParseResult has to check to make sure that the value is actually
present:

<code>
public void methodA(IntegerParseResult ipr) {
if (!ipr.hasValue()) {
throw new IllegalArgumentException();
}
if (conditionA) {
//Do something
} else {
methodB(ipr);
}
}

public void methodB(IntegerParseResult ipr) {
if (!ipr.hasValue()) {
throw new IllegalArgumentException();
}
if (conditionB) {
//Do something
} else {
methodC(ipr);
}
}

public void methodC(IntegerParseResult ipr) {
if (!ipr.hasValue()) {
throw new IllegalArgumentException();
}
if (conditionC) {
//Do something
} else {
methodA(ipr);
methodB(ipr);
}
}

public mainMethod() {
methodA(LessException.parseInt("foo"));
methodB(LessException.parseInt("25"));
methodC(LessException.parseInt("-42"));
}
</code>

I'd rather just deal with the exception right away in main, then having
to worry about it EVERYWHERE in my code.

- Oliver
 
J

Jonathan Bartlett

Even if you did, it would not be quick. An exception interpretively
combs the stack looking for a handler. Conceptually it looks as though
it pops the stack in one fell swoop, but if you benchmark the code I
think you will find it faster to just return recursively.

Really? I would have thought that they would have included a dynamic
link in the stack frame.
You might enjoy Abundance which has a feature to jaunt( leap backward
in time to a previous execution checkpoint). It does that very rapidly
by copying a stack snapshot in as the real one and restoring a few
crucial system variables.

Actually, my question was prompted by Scheme's
"call-with-current-continuation" feature. I'm looking for a good method
to introduce the feature, and was looking for similarities in other
languages. Before going whole-hog into continuations, I wanted to show
an example of non-local exits in a more familiar language. However, I
didn't want to teach a wholly bad technique.

Continuations are amazingly powerful entities, and can be used to emulate:

* threads
* generators
* backtracking/logic programming
* exceptions
* general non-local exits
* traditional program control in a web environment
* dynamic variables
* any other structured, non-local control feature

Exceptions are essentially very, very restricted continuations. So, I
thought a good way to introduce continuations is to introduce the
related concept in Java. However, it seems that I may need to find
another language with less exception overhead. Or perhaps I could try
Hawtin's suggestion of fillInStackTrace.

Also, since the discussion has turned to the use of
recursion-vs-iteration, I thought I'd throw my own two cents into the
discussion:

http://www-128.ibm.com/developerworks/linux/library/l-recurs.html

Jon
 
R

ricky.clarkson

Oliver,

You could always pass methodA, B and C the int, rather than the
IntegerParseResult.

public mainMethod()
{
methodA(LessException.parseInt("foo").getValue());
methodB...
methodC...
}

This way you will get an IllegalStateException, which is an unchecked
exception, but it's fairly obvious that there is more to the result of
parseInt than an int based on that code snippet, so an experienced
programmers shouldn't miss it.

I'd prefer the test, and there's no reason why it can't be done in
mainMethod:

public mainMethod()
{
IntegerParseResult ipr=LessExceptions.parseInt("foo");
if (ipr.hasValue())
methodA(ipr.getValue());

ipr=LessExceptions.parseInt("25");
if (ipr.hasValue())
methodB(ipr.getValue());

ipr=LessExceptions.parseInt("-42");
if (ipr.hasValue())
methodC(ipr.getValue());
}

To get the same using Integer.parseInt you would need 3 separate
try..catch blocks:

public mainMethod()
{
try
{
methodA(Integer.parseInt("foo"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}

try
{
methodB(Integer.parseInt("25"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}

try
{
methodC(Integer.parseInt("-42"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}
}

I know which I'd rather read/write/explain to newbies.
For one thing, it's
impossible to get a negative parse value, despite the existence of a
"NegativeIntegerParseResult" class.

I'm not sure what you mean by that. I meant a failed parse, the kind
of thing parseInt("foo") would return.
 
M

Mike Schilling

Roedy Green said:
Brings back memories. Very early in my career I converted a recursive
Quicksort into non-recursive FORTRAN. What a nightmare. Recursion
really simplifies some algorithms. I could not believe how much
Fortran it took to do the same thing as a few lines of Journal of the
ACM Algol.

Back in college I took an course where the textbook used Fortran and
actually implemented recursive algorithms in Fortran:

1. Declare a really big array to be used as a stack.
2. Declare labels for all the places a recursive call could be made from.
3.. Implement a recursive call by
A. copying all local variables to the next available slots in the array
B. copying the "index" of the next statement to the array
C. Update the current array index
D. GO TO the start of the subroutine
4. Implement a possibly recursive return by
A. If the array is now empty, return
B. Otherwise, restore the saved local variables, update the array index,
and use a computed GO TO to return to the site of the "call".

Horrible, isn't it?
 
R

Roedy Green

Back in college I took an course where the textbook used Fortran and
actually implemented recursive algorithms in Fortran:

Today's students use methods recursively without any sense of awe.
Consider that early machines had no stacks to support recursion. Even
the IBM 370 had no stacks. In PL/I you had to declare methods
RECURSIVE which then used an alternate less efficient calling
convention. I don't know about today, but originally FORTRAN and COBOL
had no recursion or threads for that matter.
 
L

Luc The Perverse

Roedy Green said:
Today's students use methods recursively without any sense of awe.
Consider that early machines had no stacks to support recursion. Even
the IBM 370 had no stacks. In PL/I you had to declare methods
RECURSIVE which then used an alternate less efficient calling
convention. I don't know about today, but originally FORTRAN and COBOL
had no recursion or threads for that matter.

My age group may be the last to have experienced this type of programming
environment (back in the days of GW-Basic)

I don't know how young you define "kids" but I imagine I would probably be
included. It's not completely lost :)
 
C

Chris Uppal

Jonathan said:
An exception interpretively
combs the stack looking for a handler.[...]

Really? I would have thought that they would have included a dynamic
link in the stack frame.

Java's bytecode-level representation of exception handlers is set up very
obviously for zero-cost try blocks. That's to say that /entering/ a try block
has zero overhead. The downside, of course, is that if an exception is
actually thrown, then finding and invoking the handler is not as cheap -- isn't
even constant-time.

Implementation are free to implement throw/catch how they like, but since the
hardest part of doing zero-cost try blocks is creating the stack maps, and in
Java bytecode the compiler is required to do that work for you, I think it
would be odd for a "normal" JVM implementations to use any other technique.

-- chris
 
O

Oliver Wong

Oliver,

You could always pass methodA, B and C the int, rather than the
IntegerParseResult.

public mainMethod()
{
methodA(LessException.parseInt("foo").getValue());
methodB...
methodC...
}

This way you will get an IllegalStateException, which is an unchecked
exception, but it's fairly obvious that there is more to the result of
parseInt than an int based on that code snippet, so an experienced
programmers shouldn't miss it.

parseInt() might be an expensive operation, so you'd generally want to
keep the object representing the parsed int around, in case you want to
re-use it, e.g.

mainMethod() {
ParsedInteger pInt = LessException.parseInt("foo");
methodA(pInt.getValue());
methodB(pInt.getValue());
//etc.
}

However, now you have an object which may be in and of itself "invalid",
which to me is a bad code smell. That might just be a matter of opinion, but
I prefer having the exception occur right away (during the call to
parseInt()) than to carry around a bad object, and only find out much later
on that something went wrong.

What if your application parses the int, then serializes the object in a
database.

Months later, another application running on a different computer in the
other side of the world deserializes the object, and then tries to do
something useful with it, only to get an exception thrown. This is obviously
an extreme example, but it illustrates why it's generally better to get the
exception as early as possible.
I'd prefer the test, and there's no reason why it can't be done in
mainMethod:

public mainMethod()
{
IntegerParseResult ipr=LessExceptions.parseInt("foo");
if (ipr.hasValue())
methodA(ipr.getValue());

ipr=LessExceptions.parseInt("25");
if (ipr.hasValue())
methodB(ipr.getValue());

ipr=LessExceptions.parseInt("-42");
if (ipr.hasValue())
methodC(ipr.getValue());
}


If you take the test out of methods A, B and C, and those methods are
public, then you have a fragile interface. Not too mention you also have
code duplication. Conceptually, "testing that your object has a value before
using it" and "using it" should be one indivisible action, which is why I'd
rather the test be IN the methods. But again, that might just be a matter of
opinion.
To get the same using Integer.parseInt you would need 3 separate
try..catch blocks:

public mainMethod()
{
try
{
methodA(Integer.parseInt("foo"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}

try
{
methodB(Integer.parseInt("25"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}

try
{
methodC(Integer.parseInt("-42"));
}
catch (final NumberFormatException exception)
{
logger.fine(exception);
}
}

I know which I'd rather read/write/explain to newbies.

No, actually the equivalent code using try/catch is:

public mainMethod() {
try {
methodA(Integer.parseInt("foo"));
methodB(Integer.parseInt("25"));
methodC(Integer.parseInt("-42"));
} catch(NFE e) {
logger.error(e);
}
}

Notice that with your unchecked exception algorithm that as soon as
method A executes, the exception is thrown, and we're taken OUT of the main
method. That's what the code I posted here does as well.

With your 3 try/catch blocks, what happens is that if A fails, the
program still procees and tries to do B and C. This might be what you want,
or it might not be what you want.

This added flexibility, BTW, is another good reason for having
exceptions (for those who doubt their power), though it's irrelevant now
because you've already said your problem isn't with exceptions in general,
but checked exceptions.

I'm not sure what you mean by that. I meant a failed parse, the kind
of thing parseInt("foo") would return.

The code you posted earlier for NegativeIntergerParseResult is this:

<code>
final class NegativeIntegerParseResult implements IntegerParseResult
{
public NegativeIntegerParseResult()
{
}

public boolean hasValue()
{
return false;
}

public int getValue()
{
throw new IllegalStateException();
}
}
</code>

That's what I mean by it's impossible to get a negative parse value.

- Oliver
 
O

Oliver Wong

Oliver Wong said:
though it's irrelevant now because you've already said your problem isn't
with exceptions in general, but checked exceptions.

Sorry, ignore this part. I was confusing you with a different poster.

- Oliver
 
R

ricky.clarkson

Oliver,
parseInt() might be an expensive operation, so you'd generally want to
keep the object representing the parsed int around

I'd generally not worry about how expensive the operation is until I'd
profiled the code. If I discovered that it was expensive, I'd keep a
Map said:
However, now you have an object which may be in and of itself "invalid",
which to me is a bad code smell.

There is nothing invalid about this object. It has a hasValue() and a
getValue(). hasValue() will return a boolean, getValue() will throw an
exception. If you ignore the contract on this object and just blindly
call methods at random, then you will get an IllegalStateException.
That's not a problem, that's an example of what happens when you ignore
contracts.
What if your application parses the int, then serializes the object in a
database.

ParseResult is a transient object that shouldn't be serialised - an
intermediate object. If it was supposed to be serialised it would
implement Serializable.
Notice that with your unchecked exception algorithm that as soon as
method A executes, the exception is thrown, and we're taken OUT of the main
method.

No, that's not what happens. The exception is thrown, logged and then
the method continues. Method execution continues after a catch block.
Unless you meant the first bit of code.. I suppose I should have given
them unique names instead of a few called 'mainMethod'.
This added flexibility, BTW, is another good reason for having
exceptions

What added flexibility? I'm confused there.

Ok, let's look at how to solve the code duplication issue.

String[] strings={"55","-42","foo"};

for (final String string: strings)
{
IntegerParseResult ipr=LessExceptions.parseInt(string);
if (ipr.hasValue())
methodA(ipr.getValue());
}

Still not solved, because it was methodA, methodB and methodC. I don't
like anonymous classes, but for the sake of brevity:

interface CustomRunnable
{
void run(int i);
}

String[] strings={"55","-42","foo"};

CustomRunnable[] runnables=
{
new CustomRunnable(){public void run(){methodA();}},
new CustomRunnable(){public void run(){methodB();}},
new CustomRunnable(){public void run(){methodC();}}
};

for (final int a=0;a<strings.length;a++)
{
IntegerParseResult ipr=LessExceptions.parseInt(strings[a]);

if (ipr.hasValue())
runnables[a].run(ipr.getValue());
}

In a real app I would make those anonymous classes into package-private
classes.

Alert me if I missed something - I started posting this BEFORE I looked
at the time.
 
O

Oliver Wong

Oliver,


I'd generally not worry about how expensive the operation is until I'd
profiled the code. If I discovered that it was expensive, I'd keep a
Map<String,Integer> - NOT a ParseResult.

So it seems to me like the "intended usage" of this API is to
immediately check the hasValue() method, and immediately extract the
Integer, throwing away the ParseResult. In that case, I don't see how this
is any more troublesome than working with exceptions.
There is nothing invalid about this object. It has a hasValue() and a
getValue(). hasValue() will return a boolean, getValue() will throw an
exception. If you ignore the contract on this object and just blindly
call methods at random, then you will get an IllegalStateException.
That's not a problem, that's an example of what happens when you ignore
contracts.

Maybe it's an overly painful contract then. You could imagine a factory
method whose contract were to demand that you provide a unique prime number
each time it generated a new object (and yes, it has a Set to make sure you
don't re-use prime numbers). The problem here, I think, is not with the
client's disapproval of the contract.
ParseResult is a transient object that shouldn't be serialised - an
intermediate object. If it was supposed to be serialised it would
implement Serializable.

Okay, fair enough.
No, that's not what happens. The exception is thrown, logged and then
the method continues. Method execution continues after a catch block.
Unless you meant the first bit of code.. I suppose I should have given
them unique names instead of a few called 'mainMethod'.

Yes, I was referring to the code that reads:
<code>
public mainMethod()
{
IntegerParseResult ipr=LessExceptions.parseInt("foo");
if (ipr.hasValue())
methodA(ipr.getValue());

ipr=LessExceptions.parseInt("25");
if (ipr.hasValue())
methodB(ipr.getValue());

ipr=LessExceptions.parseInt("-42");
if (ipr.hasValue())
methodC(ipr.getValue());
}
</code>

And I was saying that what you claimed was the equivalent with the 3 try
catch blocks actually is NOT equivalent. And the code that I gave with the
single try catch block IS equivalent.
What added flexibility? I'm confused there.

The added flexibility of being able to either immediately exit the
method upon an exception, or to just chug along anyway. If you work only
with unchecked exceptions and never catch them, then you are forced to
always have the first behaviour, of always exiting.
Ok, let's look at how to solve the code duplication issue.

String[] strings={"55","-42","foo"};

for (final String string: strings)
{
IntegerParseResult ipr=LessExceptions.parseInt(string);
if (ipr.hasValue())
methodA(ipr.getValue());
}

Still not solved, because it was methodA, methodB and methodC. I don't
like anonymous classes, but for the sake of brevity:

interface CustomRunnable
{
void run(int i);
}

String[] strings={"55","-42","foo"};

CustomRunnable[] runnables=
{
new CustomRunnable(){public void run(){methodA();}},
new CustomRunnable(){public void run(){methodB();}},
new CustomRunnable(){public void run(){methodC();}}
};

for (final int a=0;a<strings.length;a++)
{
IntegerParseResult ipr=LessExceptions.parseInt(strings[a]);

if (ipr.hasValue())
runnables[a].run(ipr.getValue());
}

In a real app I would make those anonymous classes into package-private
classes.

Alert me if I missed something - I started posting this BEFORE I looked
at the time.

This is just my personal opinion, but the code looks like it's getting
more and more complicated. It seems like you're fighting the language,
rather than working with it. It's like "goto" statements. They're generally
frowned upon, but there are some algorithms which are more easily expressed
with them than without.

When chosing between a language with goto statements versus a language
without, you're deciding wether the benefit of having the expressive power
of a goto statement is worth the drawback of trying to maintain code where
goto statements are mis-used.

In the example program we're working with, I think exceptions ARE worth
the extra expressive power.

- Oliver
 
R

ricky.clarkson

<code>
public mainMethod()
{
IntegerParseResult ipr=LessExceptions.parseInt("foo");
if (ipr.hasValue())
methodA(ipr.getValue());

ipr=LessExceptions.parseInt("25");
if (ipr.hasValue())
methodB(ipr.getValue());

ipr=LessExceptions.parseInt("-42");
if (ipr.hasValue())
methodC(ipr.getValue());
}
</code>

I think you misread this code.

Just a snippet:

if (ipr.hasValue())
methodA(ipr.getValue());

If ipr has a value, then methodA will not throw an
IllegalStateException, and mainMethod will continue happily.
If ipr does not have a value, then methodA will not be executed, and
mainMethod will continue happily.

At no point in that method is an IllegalStateException expected.

So, my three try blocks ARE equivalent to that code, and your one try
block ISN'T equivalent to that code.
There is as much code duplication in the three try blocks version as
there is in the version quoted above (in this post).

I could equally well (or badly!) apply the Runnable idea to the 3 try
blocks, and get equally verbose code. I agree it probably isn't as
expressive as it could be, but sometimes duplicating code leads to more
expressive code.

So the added flexibility that you mention is already there anyway, so
it's not added at all.
When chosing between a language with goto statements versus a language
without, you're deciding wether the benefit of having the expressive power
of a goto statement is worth the drawback of trying to maintain code where
goto statements are mis-used.

Not quite. It is possible to use a language without using all of its
features. That's why many projects/companies have coding standards,
and why tools like CheckStyle exist. Many programmers choose NOT to
use some features. I choose not to use autoboxing, subclassing,
non-final parameters, instanceof and casts (though these last two are
still present, but diminishing). If goto were present, I wouldn't
choose another language. I would just not use goto.
In the example program we're working with, I think exceptions ARE worth
the extra expressive power.

The extra expressive power seems to be lost to boilerplate code (lots
of try..catch blocks) to do the same thing as the program we're working
with, so I disagree there.
 
O

Oliver Wong

I think you misread this code.

You're right, I did. My bad, sorry.
Not quite. It is possible to use a language without using all of its
features. That's why many projects/companies have coding standards,
and why tools like CheckStyle exist. Many programmers choose NOT to
use some features. I choose not to use autoboxing, subclassing,
non-final parameters, instanceof and casts (though these last two are
still present, but diminishing). If goto were present, I wouldn't
choose another language. I would just not use goto.

I guess we come from different backgrounds. Because of my line of work
(compiler tools), when I am "working with" a certain programming language, I
have to be ready to deal with every possible legal construct from that
language. I guess one of the reasons I like Java so much is that I'm very
thankful it doesn't have goto statements (among other reasons).

These experiences may have inadvertently been influencing my feelings
during this thread.

- Oliver
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top