|
From: Neil M. <ne...@Cs...> - 2008-11-23 15:43:07
|
On 23 Nov 2008, at 12:45, Twylite wrote:
> I think it's time for a summary of where we are on the try/catch/
> finally.
>
> The overall intent of the TIP can be summed up as "make a control
> structure that makes dealing with exceptions, errors and resource
> cleanup simpler - both logically and visually".
>
> 1. Functionality
>
> We want to
> (a) Handle return codes, so that we can build control structures and
> handle exceptions that use return codes. In most cases an exact match
> against a single integer (or magic name) is sufficient.
> (b) Handle matching against -errorCode in the case of the return code
> TCL_ERROR (1), so that we can have something similar to other
> languages
> with typed exception handling. In most cases a prefix match, or glob
> match, or element-wise glob match on a list is sufficient.
> (aside) Any argument about the ugliness of handling return codes with
> catch+if/then or catch+switch applies equally to handing errors &
> -errorCode, and vice versa. As a result this TIP must provide for
> both
> (a) and (b), although it is not necessarily a requirement that they
> are
> provided for in the same command.
> (c) Handle on those exceptions/errors that are of interest (can be
> handled at this point) and let others propagate normally.
> (d) Handle success continuation, i.e. branch when there is no
> error/exception. This is not generally supported by procedural
> languages but the requirement has been expressed by several developers
> and TCT members.
> (e) Handle cleanup at the end of a block of code by means of a
> "finally"
> handler (regardless of errors/exceptions).
> (f) Have reasonable performance, at least for the common cases.
> (g) Discourage the use of the result for determining the nature of the
> error (an in doing so encourage the use of -errorCode). At the very
> least this means not having default support for matching on the
> result.
> (h) For exceptions thrown from handlers and finally blocks,
> maintain the
> details of the original exception (i.e. chain exceptions in the
> options
> dict).
Agree with all of these.
>
> 2. Look & Feel
>
> (a) It's going to be called [try]
> (b) Handlers are identified by keywords. The keyword "catch" has been
> argued against (confusion with existing language feature/keyword), as
> has "except" (ambiguous - "with exception" or "except for"). Likely
> candidates are "on" and "handle".
> (c) A [try] statement is going to look more like an "if {} then {}
> else
> {}" than a "switch { case {body} case {body} }". The former seems
> to be
> preferred by everyone involved in the discussions.
Agreed.
> (d) Capture of variables (return code, result and options dict)
> needs to
> happen at the front of the [try] for the statement as a whole, rather
> than per handler. This avoids confusion over which vars will be
> defined
> after the [try] returns, and also avoids variable churn if the
> errorPattern to be matched can access the variables. Although some
> amount of locality is lost, this also makes the syntax cleaner (less
> repeated "noise").
Don't entirely agree with this. I don't believe we need to care about
inconsistent sets of vars being defined after the try -- it's not a
problem for [if], [switch], and every other control structure, so I
don't believe we need to give it special consideration here. Agree
though that it is generally more useful for whatever pattern matching
mechanism is used to be called with a set of pattern/script pairs and
the variables already set-up in the callers scope. Whether that means
binding the vars for the entire try statement or once per exception
code is a matter of choice. Either seems acceptable.
> (e) A [switch]-like "fall through to next statement" would be a
> nice-to-have.
Clarifying this -- we want the ability to specify the same script for
multiple patterns (and possibly multiple exception codes). The switch
approach is one way.
>
> 3. Matching
>
> (a) In general the matching of exceptions (return code) and errors
> (errorCode where the return code is TCL_ERROR) are separate concerns.
> It makes sense to exploit this by using a fast/exact match against the
> return code first (meeting the performance requirement) followed by a
> slower match against the errorCode.
> (b) When matching against errorCode:
> (i) There is (largely) consensus that basic pattern matching is
> "good
> enough" "for now". Basic pattern matching may be defined as prefix
> matching, glob matching against errorCode (as a string), or an
> element-wise list-glob match against errorCode (as a list). In short
> there is no agreement on the right way to do this.
If adopting some novel pattern mechanism, then there is the further
question of whether to special case that in [try] or to extract it
out into a separate command (and separate TIP).
> (ii) There is also no guarantee that a match against errorCode
> will be
> adequate in the future. For example an OO-style error object may be
> developed.
> (iii) An [expr]-type match is the most flexible but the lowest
> performance (and potentially ugliest syntax); as such it is not
> suitable
> (at least not as a default).
> (iv) Delegating to [switch] for matching is a nice compromise of
> performance and flexibility (and reuses existing functionality), but
> brings with it the baggage of the [switch] command's interface.
This depends how it is done, and how it is documented. You can
delegate to [switch] either implicitly or explicitly (as a -
matchcommand) and still avoid acquiring [switch]'s interface. You
just document that pattern-matching is handled by [switch] and that
as far as [try] is concerned the patterns are just opaque data that
it passes on. Introducing an explicit option for this enhances this
rationale, as then the pattern matcher is just another callback. What
we definitely don't want to do is introduce [switch]'s various
options, like -nocase, -regexp etc as options of [try]. That would
constrain the implementation and be a mess. A callback solution
avoids this as the options can be specified as part of the callback
command, rather than as part of the try command.
> (c) The only thing we _can_ be sure of is that whatever we choose now
> will be inadequate in some what, implying that the syntax of [try]
> must
> contain provision for future extension.
> (d) Taking (c) to its logical conclusion, [try] must be specified and
> implemented to support user-selectable pattern matching. It is
> possible
> to have the matcher selected for the [try] as a whole, or per handler,
> and there are pros and cons to each approach.
>
> In terms of (d) my personal preference is to specify the matcher per
> handler. It is difficult to predict how different packages/libraries
> may approach error handling, both now and in the future (e.g. a future
> move from -errorCode to an OO-style error object). If the matcher is
> selected for the [try] as a whole it may only be possible to support
> disparate error handling styles by using the most flexible and complex
> matcher (say [expr]-based), which could be an unnecessary
> complication.
> The same holds now for integrating with legacy code that only produces
> meaningful error information in the result.
I'd be interested to see the interface proposed for this. Clearly the
most flexible approach is to allow an arbitrary script to do the
matching, but then we end up right back at the beginning of this
discussion where [try] just does exception-code dispatch and leaves
everything else up to a script. I believe we've ruled that option
out, as it violates requirements 1.b and 1.c.
>
> 4. General
>
> These are weakly-expressed requirements or requirements of my own.
>
> (a) There is a general desire for consistency / symmetry in the
> syntax.
> This would obviously improve the readability & understandability of
> the
> source code.
> (b) The behaviour of the [try] should be predictable and conform to
> the
> principle of least surprise. On particular consequence of this is
> that
> matchers must consider handlers/errorPatterns in left-to-right order,
> and all handlers should be executed in the same fashion (implying
> that
> the [try] rather than the matcher should execute the handler
> body). On
> the issue of ordering, left-to-right is the only order than makes
> sense
> for [expr]-based matching, and is the norm in other languages.
I don't believe [try] has to execute the bodies. All it has to do is
ensure that any option/result variables are defined in the calling
scope when that script runs. For example:
upvar 1 $msgVar msg $optsVar opts
set rc [catch { $script } msg opts]
invoke 1 $matchcmd [dict get $opts -errorcode] [dict get $handlers
$rc]
should do the right thing for most matching constructs.
I also believe the order in which to consider patterns should be left
to the match command.
[... snip proposal: I'll post a separate message for that ...]
-- Neil
This message has been checked for viruses but the contents of an attachment
may still contain software viruses, which could damage your computer system:
you are advised to perform your own checks. Email communications with the
University of Nottingham may be monitored as permitted by UK legislation.
|