Path: csiph.com!news.bbs.geek.nz!.POSTED.agency.bbs.geek.nz!not-for-mail From: Patrick Roemer (Patrick Roemer) Newsgroups: comp.lang.java.programmer Subject: Re: Two exceptions Date: Mon, 11 Dec 2017 15:09:14 +1200 Organization: fsxNet Usenet Gateway | bbs.geek.nz/#fsxNet Message-ID: <1592952094@f38.n261.z1.binkp.net> References: <168218953@f38.n261.z1.binkp.net> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Injection-Info: news.bbs.geek.nz; posting-host="M6YmRdZYyc42DJk0lNlt/X4dpP4dzvceBNabSmESN3E"; logging-data="22966"; mail-complaints-to="abuse@news.bbs.geek.nz" User-Agent: VSoup v1.2.9.47Beta [95/NT] X-Comment-To: Eric Sosman X-MailConverter: SoupGate-Win32 v1.05 Xref: csiph.com comp.lang.java.programmer:37808 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