Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #37737 > unrolled thread

Two exceptions

Started byVictor Porton <porton@narod.ru>
First post2017-12-09 00:53 +0200
Last post2017-12-11 15:09 +0100
Articles 12 — 6 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  Two exceptions Victor Porton <porton@narod.ru> - 2017-12-09 00:53 +0200
    Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-12-08 18:21 -0500
      Re: Two exceptions Victor Porton <porton@narod.ru> - 2017-12-09 01:55 +0200
        Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-12-09 07:39 -0500
          Re: Two exceptions Victor Porton <porton@narod.ru> - 2017-12-10 02:39 +0200
            Re: Two exceptions Victor Porton <porton@narod.ru> - 2017-12-10 02:46 +0200
            Re: Two exceptions Kiwi User <martin@mydomain.invalid> - 2017-12-10 00:52 +0000
        Re: Two exceptions Patrick Roemer <sangamon@netcologne.de> - 2017-12-09 13:47 +0100
    Re: Two exceptions Arne Vajhøj <arne@vajhoej.dk> - 2017-12-08 18:49 -0500
      Re: Two exceptions v_borchert@despammed.com (Volker Borchert) - 2017-12-09 07:53 +0000
    Re: Two exceptions Eric Sosman <esosman@comcast-dot-net.invalid> - 2017-12-09 14:55 -0500
      Re: Two exceptions Patrick Roemer <sangamon@netcologne.de> - 2017-12-11 15:09 +0100

#37737 — Two exceptions

FromVictor Porton <porton@narod.ru>
Date2017-12-09 00:53 +0200
SubjectTwo exceptions
Message-ID<p0f54o$n5f$1@gioia.aioe.org>
I am writing something like a recursive descent parser: That is, I inspect 
object and its parts. When a part of an object is wrong, my method parse() 
throws my exception ParseException. It is sometimes handled in parse() of a 
higher-level object.

Example:

An object X may be either X1 or X2 or X3 class.

I try to call parse() for an object of X1 class. If it parses, the higher 
level parse() (for X) return X1 casted to X. If it fails with exception 
ParseException, X2 is tried, and if that fails too X3 is tried.

Or for an object X I try calling parse() for subobjects Y and Z. If either 
throws ParseException, the parse exception is propagated for parse() of X.

In this way, I consider an object and its possible parts. This is sometimes 
called recursive descent.

But I want also FatalParseException meant to stop parsing of a file 
altogether if it happens in the object or its subobject.

Human logic suggests that FatalParseException is a kind of ParseException 
and so should be its subclass. But this way handling ParseException we would 
also handle FatalParseException what is wrong, because FatalParseException 
is meant to be handled only in the outer method, outside of all parse() 
methods.

So I have two alternatives:

1. Derive ParseException from FatalParseException what seems to do the right 
thing but seems illogical.

2. Not derive either of these two exceptions from each other.

By the way, I am to rewrite an Ada project in Java (because the Ada compiler 
is too buggy and I can't work with it). In Ada all exceptions are 
independent (no exception is a subclass of another one).

-- 
Victor Porton - http://portonvictor.org

[toc] | [next] | [standalone]


#37738

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2017-12-08 18:21 -0500
Message-ID<p0f6p8$256$1@dont-email.me>
In reply to#37737
On 12/8/2017 5:53 PM, Victor Porton wrote:
> I am writing something like a recursive descent parser: That is, I inspect
> object and its parts. When a part of an object is wrong, my method parse()
> throws my exception ParseException. It is sometimes handled in parse() of a
> higher-level object.
> 
> Example:
> 
> An object X may be either X1 or X2 or X3 class.
> 
> I try to call parse() for an object of X1 class. If it parses, the higher
> level parse() (for X) return X1 casted to X. If it fails with exception
> ParseException, X2 is tried, and if that fails too X3 is tried.
> 
> Or for an object X I try calling parse() for subobjects Y and Z. If either
> throws ParseException, the parse exception is propagated for parse() of X.
> 
> In this way, I consider an object and its possible parts. This is sometimes
> called recursive descent.
> 
> But I want also FatalParseException meant to stop parsing of a file
> altogether if it happens in the object or its subobject.
> 
> Human logic suggests that FatalParseException is a kind of ParseException
> and so should be its subclass. But this way handling ParseException we would
> also handle FatalParseException what is wrong, because FatalParseException
> is meant to be handled only in the outer method, outside of all parse()
> methods.
> 
> So I have two alternatives:
> 
> 1. Derive ParseException from FatalParseException what seems to do the right
> thing but seems illogical.
> 
> 2. Not derive either of these two exceptions from each other.
> 
> By the way, I am to rewrite an Ada project in Java (because the Ada compiler
> is too buggy and I can't work with it). In Ada all exceptions are
> independent (no exception is a subclass of another one).

     Alternative #1 seems preferable (to me).  Perhaps you would find it
more palatable if you renamed FatalParseException to ParseFailure and
ParseException to ParseMismatch.

     However, I feel you may be misusing exceptions.  They are supposed
to be, well, "exceptional" rather than "normal control flow."  In a
recursive descent parser you *expect* many matching failures, just in
the ordinary course of events; a matching failure is not "exceptional."
(Also, building a Throwable can be rather expensive, especially if it
is thrown from a deep point in the call stack and must fill in a long
stack trace.  Java's optimizations tend to focus on the "normal" cases
rather than on the "exceptional" ones, so throwing and catching, even
though much improved since Java 1.0, still involves a lot of work.)

     Instead of announcing a "venial" mismatch by throwing an exception,
consider returning a status value from each parse method.  This might
be a boolean ("It's an X1" / "It's not an X1") or a reference value
("It's an X1 and here it is" / "It's not an X1 and here's a null"),
or something more structured ("Here's a status-and-result-and-info
object; look at its fields to find out what happened").  Not only is
this likely to produce faster code, but it may also simplify

	X result;
	try {
	    result = X1.parse();
	} catch (ParseMismatch ex1) {
	    try {
	        result = X2.parse();
	    } catch (ParseMismatch ex2) {
	        try {
	            result = X3.parse();
	        } catch (ParseMismatch ex3) {
	            ...
	}

to something more like

	X result = X1.parse();
	if (result == null)
	    result = X2.parse();
	if (result == null)
	    result = X3.parse();
	...

(You could, of course, nest these differently.)

     Perhaps the structure of the existing Ada code is not amenable
to this sort of transformation -- but if I were you I'd give it
some consideration.

-- 
esosman@comcast-dot-net.invalid
Eleven hundred thirty-nine days to go.

[toc] | [prev] | [next] | [standalone]


#37740

FromVictor Porton <porton@narod.ru>
Date2017-12-09 01:55 +0200
Message-ID<p0f8pt$s13$1@gioia.aioe.org>
In reply to#37738
Eric Sosman wrote:

> On 12/8/2017 5:53 PM, Victor Porton wrote:
>> I am writing something like a recursive descent parser: That is, I
>> inspect object and its parts. When a part of an object is wrong, my
>> method parse() throws my exception ParseException. It is sometimes
>> handled in parse() of a higher-level object.
>> 
>> Example:
>> 
>> An object X may be either X1 or X2 or X3 class.
>> 
>> I try to call parse() for an object of X1 class. If it parses, the higher
>> level parse() (for X) return X1 casted to X. If it fails with exception
>> ParseException, X2 is tried, and if that fails too X3 is tried.
>> 
>> Or for an object X I try calling parse() for subobjects Y and Z. If
>> either throws ParseException, the parse exception is propagated for
>> parse() of X.
>> 
>> In this way, I consider an object and its possible parts. This is
>> sometimes called recursive descent.
>> 
>> But I want also FatalParseException meant to stop parsing of a file
>> altogether if it happens in the object or its subobject.
>> 
>> Human logic suggests that FatalParseException is a kind of ParseException
>> and so should be its subclass. But this way handling ParseException we
>> would also handle FatalParseException what is wrong, because
>> FatalParseException is meant to be handled only in the outer method,
>> outside of all parse() methods.
>> 
>> So I have two alternatives:
>> 
>> 1. Derive ParseException from FatalParseException what seems to do the
>> right thing but seems illogical.
>> 
>> 2. Not derive either of these two exceptions from each other.
>> 
>> By the way, I am to rewrite an Ada project in Java (because the Ada
>> compiler is too buggy and I can't work with it). In Ada all exceptions
>> are independent (no exception is a subclass of another one).
> 
>      Alternative #1 seems preferable (to me).  Perhaps you would find it
> more palatable if you renamed FatalParseException to ParseFailure and
> ParseException to ParseMismatch.
> 
>      However, I feel you may be misusing exceptions.  They are supposed
> to be, well, "exceptional" rather than "normal control flow."  In a
> recursive descent parser you *expect* many matching failures, just in
> the ordinary course of events; a matching failure is not "exceptional."
> (Also, building a Throwable can be rather expensive, especially if it
> is thrown from a deep point in the call stack and must fill in a long
> stack trace.  Java's optimizations tend to focus on the "normal" cases
> rather than on the "exceptional" ones, so throwing and catching, even
> though much improved since Java 1.0, still involves a lot of work.)
> 
>      Instead of announcing a "venial" mismatch by throwing an exception,
> consider returning a status value from each parse method.  This might
> be a boolean ("It's an X1" / "It's not an X1") or a reference value
> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
> or something more structured ("Here's a status-and-result-and-info
> object; look at its fields to find out what happened").  Not only is
> this likely to produce faster code, but it may also simplify
> 
> X result;
> try {
> result = X1.parse();
> } catch (ParseMismatch ex1) {
> try {
> result = X2.parse();
> } catch (ParseMismatch ex2) {
> try {
> result = X3.parse();
> } catch (ParseMismatch ex3) {
> ...
> }
> 
> to something more like
> 
> X result = X1.parse();
> if (result == null)
> result = X2.parse();
> if (result == null)
> result = X3.parse();
> ...

The main reason I chose to use exceptions for this, is to allow parse() 
method to return the result of parsing (not a boolean value). For example 
result of parsing an integer may return "long".

Can you propose another alternative? It is to be easily programmable (if I 
find handling exceptions too slow, I always can rewrite my program).

Oh, I see, you proposed to return "null" instead of my ParseException. It 
however also seems to me not a quite good solution, because returning null 
may be a non-exceptionable situation (as for a missing optional object in 
the parsed resource).

> (You could, of course, nest these differently.)
> 
>      Perhaps the structure of the existing Ada code is not amenable
> to this sort of transformation -- but if I were you I'd give it
> some consideration.
> 
-- 
Victor Porton - http://portonvictor.org

[toc] | [prev] | [next] | [standalone]


#37773

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2017-12-09 07:39 -0500
Message-ID<p0glhg$abn$1@dont-email.me>
In reply to#37740
On 12/8/2017 6:55 PM, Victor Porton wrote:
> Eric Sosman wrote:
> 
>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>> [... using exceptions to control a recursive descent parser ...]
>> [... maybe exceptions aren't the best tool ...]
>>       Instead of announcing a "venial" mismatch by throwing an exception,
>> consider returning a status value from each parse method.  This might
>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>> or something more structured ("Here's a status-and-result-and-info
>> object; look at its fields to find out what happened").  [...]
> 
> The main reason I chose to use exceptions for this, is to allow parse()
> method to return the result of parsing (not a boolean value). For example
> result of parsing an integer may return "long".
> 
> Can you propose another alternative? It is to be easily programmable (if I
> find handling exceptions too slow, I always can rewrite my program).
> 
> Oh, I see, you proposed to return "null" instead of my ParseException. It
> however also seems to me not a quite good solution, because returning null
> may be a non-exceptionable situation (as for a missing optional object in
> the parsed resource).

     If `null' is a legitimate result (so you can't use a `null' return
to mean "Not matched"), use a status-and-result-and-info object like

	class ParseResult {
	    final boolean matched;  // true = success, false = failure
	    final Object result;  // only relevant if matched == true
	    // ... other info as desired ...
	}

... and return a ParseResult instance from each parse() method.  The
`result' field might be `null', but if `matched' is true the `null' is
a "real" `null', not a failure indication.

-- 
esosman@comcast-dot-net.invalid
Eleven hundred thirty-eight days to go.

[toc] | [prev] | [next] | [standalone]


#37785

FromVictor Porton <porton@narod.ru>
Date2017-12-10 02:39 +0200
Message-ID<p0hvoe$p67$1@gioia.aioe.org>
In reply to#37773
Eric Sosman wrote:

> On 12/8/2017 6:55 PM, Victor Porton wrote:
>> Eric Sosman wrote:
>> 
>>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>>> [... using exceptions to control a recursive descent parser ...]
>>> [... maybe exceptions aren't the best tool ...]
>>>       Instead of announcing a "venial" mismatch by throwing an
>>>       exception,
>>> consider returning a status value from each parse method.  This might
>>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>>> or something more structured ("Here's a status-and-result-and-info
>>> object; look at its fields to find out what happened").  [...]
>> 
>> The main reason I chose to use exceptions for this, is to allow parse()
>> method to return the result of parsing (not a boolean value). For example
>> result of parsing an integer may return "long".
>> 
>> Can you propose another alternative? It is to be easily programmable (if
>> I find handling exceptions too slow, I always can rewrite my program).
>> 
>> Oh, I see, you proposed to return "null" instead of my ParseException. It
>> however also seems to me not a quite good solution, because returning
>> null may be a non-exceptionable situation (as for a missing optional
>> object in the parsed resource).
> 
>      If `null' is a legitimate result (so you can't use a `null' return
> to mean "Not matched"), use a status-and-result-and-info object like
> 
> class ParseResult {
> final boolean matched;  // true = success, false = failure
> final Object result;  // only relevant if matched == true
> // ... other info as desired ...
> }

I haven't considered this perhaps because Ada does not allow a class to be a 
member of object (without wrapping it into something called 
Indefinite_Wrapper or something similar). So in Ada this would be 
inconvenient.

> ... and return a ParseResult instance from each parse() method.  The
> `result' field might be `null', but if `matched' is true the `null' is
> a "real" `null', not a failure indication.
> 
-- 
Victor Porton - http://portonvictor.org

[toc] | [prev] | [next] | [standalone]


#37786

FromVictor Porton <porton@narod.ru>
Date2017-12-10 02:46 +0200
Message-ID<p0i04h$pm2$1@gioia.aioe.org>
In reply to#37785
Victor Porton wrote:

> Eric Sosman wrote:
> 
>> On 12/8/2017 6:55 PM, Victor Porton wrote:
>>> Eric Sosman wrote:
>>> 
>>>> On 12/8/2017 5:53 PM, Victor Porton wrote:
>>>>> [... using exceptions to control a recursive descent parser ...]
>>>> [... maybe exceptions aren't the best tool ...]
>>>>       Instead of announcing a "venial" mismatch by throwing an
>>>>       exception,
>>>> consider returning a status value from each parse method.  This might
>>>> be a boolean ("It's an X1" / "It's not an X1") or a reference value
>>>> ("It's an X1 and here it is" / "It's not an X1 and here's a null"),
>>>> or something more structured ("Here's a status-and-result-and-info
>>>> object; look at its fields to find out what happened").  [...]
>>> 
>>> The main reason I chose to use exceptions for this, is to allow parse()
>>> method to return the result of parsing (not a boolean value). For
>>> example result of parsing an integer may return "long".
>>> 
>>> Can you propose another alternative? It is to be easily programmable (if
>>> I find handling exceptions too slow, I always can rewrite my program).
>>> 
>>> Oh, I see, you proposed to return "null" instead of my ParseException.
>>> It however also seems to me not a quite good solution, because returning
>>> null may be a non-exceptionable situation (as for a missing optional
>>> object in the parsed resource).
>> 
>>      If `null' is a legitimate result (so you can't use a `null' return
>> to mean "Not matched"), use a status-and-result-and-info object like
>> 
>> class ParseResult {
>> final boolean matched;  // true = success, false = failure
>> final Object result;  // only relevant if matched == true
>> // ... other info as desired ...
>> }
> 
> I haven't considered this perhaps because Ada does not allow a class to be
> a member of object (without wrapping it into something called
> Indefinite_Wrapper or something similar). So in Ada this would be
> inconvenient.

Hm, well, I should have considered this. In Ada this can be made with a 
constructor function (just like as in Java) and an accessor hiding 
Indefinite_Wrapper as a details of implementation. Weird that I have not 
considered this. Well, maybe this is because I didn't wonder about 
performance very much and considered many exceptions OK.

But thanks for the idea. I think, I will follow your advice and remove 
ParseException altogether leaving only FatalParseException.

>> ... and return a ParseResult instance from each parse() method.  The
>> `result' field might be `null', but if `matched' is true the `null' is
>> a "real" `null', not a failure indication.
>> 
-- 
Victor Porton - http://portonvictor.org

[toc] | [prev] | [next] | [standalone]


#37788

FromKiwi User <martin@mydomain.invalid>
Date2017-12-10 00:52 +0000
Message-ID<p0i0fv$poq$1@news.albasani.net>
In reply to#37785
On Sun, 10 Dec 2017 02:39:33 +0200, Victor Porton wrote:

> I haven't considered this perhaps because Ada does not allow a class to
> be a member of object (without wrapping it into something called
> Indefinite_Wrapper or something similar). So in Ada this would be
> inconvenient.
>
This looks like the best solution suggested so far, particularly if it is 
expanded to include an error message and provide a boolean isError() 
method.


-- 
Martin    | martin at
Gregorie  | gregorie
          | dot org

[toc] | [prev] | [next] | [standalone]


#37774

FromPatrick Roemer <sangamon@netcologne.de>
Date2017-12-09 13:47 +0100
Message-ID<p0gm14$bmr$1@newsreader4.netcologne.de>
In reply to#37740
Responding to Victor Porton:
> The main reason I chose to use exceptions for this, is to allow parse() 
> method to return the result of parsing (not a boolean value). For example 
> result of parsing an integer may return "long".
> 
> Can you propose another alternative? It is to be easily programmable (if I 
> find handling exceptions too slow, I always can rewrite my program).
> 
> Oh, I see, you proposed to return "null" instead of my ParseException. It 
> however also seems to me not a quite good solution, because returning null 
> may be a non-exceptionable situation (as for a missing optional object in 
> the parsed resource).

Instead of null, you could use (potentially nested) java.util.Optional
for both, signaling failed parse attempts and optional productions.

failure: empty
success (plain): of(x)
success (optional, non-match): of(empty)
success (optional, match): of(of(x))

Preferably, you could introduce a specialized union type for the parse
result, allowing you to propagate detail information in the failure case.

See the Reply type here, for example:

https://github.com/jon-hanson/parsecj#types

Best regards,
Patrick

[toc] | [prev] | [next] | [standalone]


#37739

FromArne Vajhøj <arne@vajhoej.dk>
Date2017-12-08 18:49 -0500
Message-ID<p0f8eu$rir$1@gioia.aioe.org>
In reply to#37737
On 12/8/2017 5:53 PM, Victor Porton wrote:
> I am writing something like a recursive descent parser: That is, I inspect
> object and its parts. When a part of an object is wrong, my method parse()
> throws my exception ParseException. It is sometimes handled in parse() of a
> higher-level object.
> 
> Example:
> 
> An object X may be either X1 or X2 or X3 class.
> 
> I try to call parse() for an object of X1 class. If it parses, the higher
> level parse() (for X) return X1 casted to X. If it fails with exception
> ParseException, X2 is tried, and if that fails too X3 is tried.
> 
> Or for an object X I try calling parse() for subobjects Y and Z. If either
> throws ParseException, the parse exception is propagated for parse() of X.
> 
> In this way, I consider an object and its possible parts. This is sometimes
> called recursive descent.
> 
> But I want also FatalParseException meant to stop parsing of a file
> altogether if it happens in the object or its subobject.
> 
> Human logic suggests that FatalParseException is a kind of ParseException
> and so should be its subclass. But this way handling ParseException we would
> also handle FatalParseException what is wrong, because FatalParseException
> is meant to be handled only in the outer method, outside of all parse()
> methods.
> 
> So I have two alternatives:
> 
> 1. Derive ParseException from FatalParseException what seems to do the right
> thing but seems illogical.
> 
> 2. Not derive either of these two exceptions from each other.

First you need to decide if you really want to use exceptions for this.
It is usually frowned on to use exceptions to control flow. But OK
the Java API has some parse methods throwing checked exceptions.

If you want to proceed this route then I would go for:

3) Base-class ParseException with two sub-classes FatalParseException
    and NonFatalParseException.

I think that is the OO representation of your domain logic.

Arne

[toc] | [prev] | [next] | [standalone]


#37772

Fromv_borchert@despammed.com (Volker Borchert)
Date2017-12-09 07:53 +0000
Message-ID<p0g4qh$gkm$3@Gaia.teknon.de>
In reply to#37739
Arne Vajh??j wrote:

> If you want to proceed this route then I would go for:
> 
> 3) Base-class ParseException with two sub-classes FatalParseException
>     and NonFatalParseException.

Second that.

-- 

"I'm a doctor, not a mechanic." Dr Leonard McCoy <mccoy@ncc1701.starfleet.fed>
"I'm a mechanic, not a doctor." Volker Borchert  <v_borchert@despammed.com>

[toc] | [prev] | [next] | [standalone]


#37776

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2017-12-09 14:55 -0500
Message-ID<p0hf3r$37q$1@dont-email.me>
In reply to#37737
On 12/9/2017 11:15 AM, Stefan Ram wrote:
> Victor Porton <porton@narod.ru> writes:
>> So I have two alternatives:
>> 1. Derive ParseException from FatalParseException what seems to do the right
>> thing but seems illogical.
> 
>    The type of a result of a parse is
> 
> class ParseResult { ...
> 
>    which can have subclasses
> 
> class ProperParseResult extends ParseResult { ...
> class ErrorParseResult extends ParseResult { ...
> 
>    which in turn can have several subclasses each of which
>    might implement several interfaces.
> 
>    A parseResult might contain other parseResults (AST).

     This would work, yes, but I think it would lead to clumsy
code at the call site, something like

	ParseResult result = parse();
	if (result instanceof ProperParseResult) {
	   ProperParseResult ppr = (ProperParseResult) result;
	   ...
	} else if (result instanceof ErrorParseResult) {
	    ErrorParseResult epr = (ErrorParseResult) result;
	    ...
	} else {
	    throw new IllegalStateException("bad result", result);
	}

That is, you'd need to use `instanceof' (or equivalent) to learn
what kind of a result you obtained -- and if you find yourself
using `instanceof' a lot, that's usually a sign that the design
could be improved.

     (It can be argued that `try/catch' is a disguised `instanceof',
particularly if there are multiple `catch' clauses.  This seems
forgivable, though, on the grounds that exceptions are "unusual.")

-- 
esosman@comcast-dot-net.invalid
Eleven hundred thirty-eight days to go.

[toc] | [prev] | [next] | [standalone]


#37805

FromPatrick Roemer <sangamon@netcologne.de>
Date2017-12-11 15:09 +0100
Message-ID<p0m3ib$g4v$1@newsreader4.netcologne.de>
In reply to#37776
Responding to Eric Sosman:
> On 12/9/2017 11:15 AM, Stefan Ram wrote:
>>    The type of a result of a parse is
>> 
>> class ParseResult { ...
>> 
>>    which can have subclasses
>> 
>> class ProperParseResult extends ParseResult { ...
>> class ErrorParseResult extends ParseResult { ...
>> 
>>    which in turn can have several subclasses each of which
>>    might implement several interfaces.
>> 
>>    A parseResult might contain other parseResults (AST).
> 
>      This would work, yes, but I think it would lead to clumsy
> code at the call site, something like
> 
> 	ParseResult result = parse();
> 	if (result instanceof ProperParseResult) {
> 	   ProperParseResult ppr = (ProperParseResult) result;
> 	   ...
> 	} else if (result instanceof ErrorParseResult) {
> 	    ErrorParseResult epr = (ErrorParseResult) result;
> 	    ...
> 	} else {
> 	    throw new IllegalStateException("bad result", result);
> 	}

Not necessarily, or rather, not at all. Ignoring the AST suggestion...

ParseResult could declare a #matched() method, equivalent to the
#matched field in your single-class example. Handling would be identical
to your suggestion, but the types would be clearer, IMHO. Members that
are "only relevant if" another member carries a certain value feels like
a code smell to me, too.

There could also be a ParseResult#resultValue() method that throws in
the ErrorParseResult case. This would give the top level caller the same
experience as in the exception-signaling approach, but based on a
cleaner implementation.

Furthermore there could be a ParseResult#fold[T]() method that takes two
lambdas, Object -> T and Throwable -> T, and invokes the corresponding
lambda for the success resp. error case.

Finally, the instanceof cascade could be replaced with a proper Visitor
implementation over ParseResult. Stefan mentioned the Visitor pattern,
but constrained it to the tree traversal context. It is primarily about
double dispatch, though, and would apply nicely to the ParseResult
hierarchy, where there's no tree in sight.

Actually, all the approaches listed above could be supported in
parallel, leaving the result resolution style to the discretion of the
caller - which is what the author(s) of parsecj[1] opted for (excluding
the Visitor option).

The next step from this approach might be the introduction of
combinators that merge multiple results into one.

Best regards,
Patrick

[1] https://github.com/jon-hanson/parsecj#types

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web