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


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

Two exceptions

Started byVictor Porton <porton@narod.ru> (Victor Porton)
First post2017-12-09 00:53 +1200
Last post2017-12-12 11:14 +0200
Articles 15 — 9 participants

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


Contents

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

#37777 — Two exceptions

FromVictor Porton <porton@narod.ru> (Victor Porton)
Date2017-12-09 00:53 +1200
SubjectTwo exceptions
Message-ID<4199194256@f38.n261.z1.binkp.net>
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]


#37778

FromEric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman)
Date2017-12-08 18:21 +1200
Message-ID<2386781254@f38.n261.z1.binkp.net>
In reply to#37777
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]


#37780

FromVictor Porton <porton@narod.ru> (Victor Porton)
Date2017-12-09 01:55 +1200
Message-ID<1735587349@f38.n261.z1.binkp.net>
In reply to#37778
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]


#37782

FromEric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman)
Date2017-12-09 07:39 +1200
Message-ID<1984930110@f38.n261.z1.binkp.net>
In reply to#37780
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]


#37796

FromVictor Porton <porton@narod.ru> (Victor Porton)
Date2017-12-10 02:39 +1200
Message-ID<3640044093@f38.n261.z1.binkp.net>
In reply to#37782
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]


#37797

FromVictor Porton <porton@narod.ru> (Victor Porton)
Date2017-12-10 02:46 +1200
Message-ID<378316829@f38.n261.z1.binkp.net>
In reply to#37796
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]


#37799

FromKiwi User <martin@mydomain.invalid> (Kiwi User)
Date2017-12-10 00:52 +1200
Message-ID<2064065438@f38.n261.z1.binkp.net>
In reply to#37796
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]


#37804

FromKiwi User <martin@mydomain.invalid>
Date2017-12-10 21:50 +0000
Message-ID<p0ka7a$v6a$1@news.albasani.net>
In reply to#37799
On Sun, 10 Dec 2017 00:52:14 +1200, Kiwi User wrote:

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

Gaaaagh: Misplaced comment: I meant to say that I thought Eric's 
ParseResult class looked, to me anyway, like the best solution I've seen 
so far. 

It deals with all possible results neatly and doesn't need to use 
exception handling for unexceptional results (matched, didn't match, 
unrecognised input) so it leaves Exceptions free to handle parser 
internal errors AND, if extended to hold error descriptions, it provides 
a neat way of returning error messages without having to pass an error 
reporting channel reference to every operation on the recursive parse 
stack. 


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

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


#37783

FromPatrick Roemer <sangamon@netcologne.de> (Patrick Roemer)
Date2017-12-09 13:47 +1200
Message-ID<2326571928@f38.n261.z1.binkp.net>
In reply to#37780
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]


#37779

FromArne Vajhøj <arne@vajhoej.dk> (Arne Vajhøj)
Date2017-12-08 18:49 +1200
Message-ID<3203151262@f38.n261.z1.binkp.net>
In reply to#37777
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]


#37781

Fromv_borchert@despammed.com (Volker Borchert) (Volker Borchert)
Date2017-12-09 07:53 +1200
Message-ID<1606898186@f38.n261.z1.binkp.net>
In reply to#37779
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]


#37784

Fromram@zedat.fu-berlin.de (Stefan Ram) (Stefan Ram)
Date2017-12-09 16:15 +1200
Message-ID<3577663232@f38.n261.z1.binkp.net>
In reply to#37777
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).

  But without knowing more details of the problem, it is
  difficult to suggest further details.

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

  When X is not X1, of course, this is neither an error
  nor a reason to throw.

  (Check whether you might use the anti-pattern "switch on
  (sub)type [i.e., X1, X2, X3]" whose use should be replaced
  by polymorphism.)

  Traversing given nested objects is not called "parsing"
  (which is working on a sequence of tokens), it is more like
  a tree travesal, which might suggest to check out the
  visitor pattern.

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


#37793

FromEric Sosman <esosman@comcast-dot-net.invalid> (Eric Sosman)
Date2017-12-09 14:55 +1200
Message-ID<168218953@f38.n261.z1.binkp.net>
In reply to#37784
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]


#37808

FromPatrick Roemer <sangamon@netcologne.de> (Patrick Roemer)
Date2017-12-11 15:09 +1200
Message-ID<1592952094@f38.n261.z1.binkp.net>
In reply to#37793
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] | [next] | [standalone]


#37812

FromVictor Porton <porton@narod.ru>
Date2017-12-12 11:14 +0200
Message-ID<p0o6m8$d77$1@gioia.aioe.org>
In reply to#37808
Patrick Roemer wrote:
> 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

You can see my actual code which I have written (without exceptions
except of FatalParseError) at

https://github.com/vporton/xml-boiler/tree/master/core/src/main/java/org/boiler/rdf_recursive_descent

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

[toc] | [prev] | [standalone]


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


csiph-web