|
From: Twylite <tw...@cr...> - 2008-11-20 22:14:14
|
Hi,
> It's not a range - the "-" in switch means "use the same body as the
> next branch". i.e., the example means codes 0 and 4 (ok and continue),
> not 0 to 4.
Ah. My bad. A violation of rule #2: never engage in a debate when the
coffee has run out.
I had been thinking about catching ranges of return codes (e.g. all
user-defined codes as opposed to Tcl-reserved codes) and wasn't paying
enough attention to the code.
> OK. On reflection I'm willing to concede that adequate error case
> analysis requires pattern matching of some sort built-in to [try].
Yay :)
>> Of course its entirely possible that some bright spark declares that if
>> the general class of errorCode is "OBJECT" then [lindex $errorCode 1] is
>> an oo::object that is a child of tcl::errorobj, and you want to do
>> class-based matching on said object. This is probably quite a strong
>> argument against glob matching (as the only option).
> Good point. I hadn't thought of that. That also could handle
> subtype-based matching.
>> So I have been suggesting "string match" on a structured list as an
>> approximation of the 'isa' / 'typeof' operator.
> Which could be workable: the OO exception just dumps [$self info
> ancestors] or whatever into the errorCode, so you have something like:
> [list Error IOError HostUnreachable object12].
That certainly has the potential to work. Constructing the error object
(or calling [errorobj throw]) - however you want it to work - would do a
[return -options {...}] putting the object into the options dict, but
also [$self info ancestors] into -errorcode.
>> My point here applies most specifically to cases where you don't control
>> the API (which is rather common), but also to cases where you don't want
>> to modify the API - because it will affect other working code that you
>> don't want to refactor, or because you believe that adding a flow
>> control statement like 'break' or 'continue' outside of a flow control
>> construct is an inherently dangerous code practice because developers
>> using APIs don't expect stuff like that.
> Well, all exceptions affect flow control.
I humbly submit that while one can do immensely cool things with Tcl,
the average developer (indeed, a good number of well-above-average
developers) will adopt the WTF face when confronted with the idea that a
procedure can throw a continue. We live in the Victorianesque era of
structure code, and one simply does not abide by such vulgarity in
civilised society.
>>> proc connect {schemes host user pass} {
>>> foreach scheme $schemes {
>>> try {
>>> return [$scheme connect $host $user $pass]
>>> } on error BADAUTH {} { continue }
>>> }
>>> error "unable to authenticate"
>>> }
> I agree that errorcode is pretty unlikely to be useful outside of
> errors, hence making it optional. I still quite like the symmetry of
> this proposal.
The symmetry is attractive, but I can't help feeling that if we are
trying to shoehorn exception handling and error handling into the same
construct then a reasonable amount of asymmetry is expected. In fact a
clear distinction between handling errors and handling exceptions may
make the intent of the code more evident.
>> Matching:
>> c. Possibly provide for more complex matches involving other fields
>> (result), regular expressions, disjunctive matching, case sensitivity,
>> relational calculus/algebra, whatever.
> Ha! Maybe subsumption based matching using a description logic
> reasoner? :-)
.. strange ... I though Girl Genius was in the other tab.
> It seems clear that matching on a the return code (or a list of) can
> be done in O(1). It seems we agree that this can and should be
> supported. How much to support narrowing down beyond that is the main
> focus of debate. I'm prepared to agree with you that some sort of
> errorCode matching would be beneficial. Glob-matching errorCode is
> reasonably cheap and probably does cover most common cases. Another
> cheap alternative would be to treat the pattern as simply a list of
> constants and then match by finding longest common prefix between the
> matches and the errorcode, which would have worst case O(N) where N is
> the llength of $errorCode. That also has the advantage that you can
> compile all catch blocks into a single pattern matching tree
> (trie-like). While customised pattern matching is appealing, it seems
> likely to involve extra complexity in both usage and implementation,
> with probably performance impacts too. User-supplied predicates or
> customisable pattern matching is likely to be much less efficient, so
> should probably not be the default.
Mmm ... the performance vs flexibility trade-off. The compromise is
usually to allow the user to select which they need for the particular
case. This one deserves more thought, possibly at a less reasonable
hour (like in the morning).
>> try {
>> # ...
>> } thenwith -glob -- "%C,%E" {
>> "1,POSIX *" { handle posix errors }
>> "3,*" -
>> "4,*" { handle break/continue }
>> }
> I don't think it matters whether [try] delegates to [switch] in the
> implementation. This still results in [try] acquiring the interface of
> [switch].
True, but also the full functionality of switch, and any enhancements
made to switch. Matching is not limited to a particular approach that
I/we think is appropriate right now, but has the flexibility to address
a wide range of needs.
>> (2) Make [try] an unholy union of [catch] and [if]/then/else, and
>> provide helper functions/operations to match exception/error cases with
>> expr.
>> try {
>> # ...
>> } handle { [string match "POSIX *" $::errorCode] } {
>> handle posix errors
>> }
> I briefly considered something along those lines -- general predicates
> as guards. I think it's getting too complex though. As you say, [try]
> doesn't need to handle all cases -- it just needs to handle the most
> common cases decently.
Oh, to have a convenient and provably accurate definition of "common
cases" ;)
> Just ignore me and go with glob-matching or prefix matching. So long
> as non-error exceptions are handled well, I'll be happy.
I'd prefer not to -- your approach is often different to mine and it
pays to consider all views and comments when designing a feature like
this. I had already started breaking down the separate concerns, look &
feel and matching options before this particular thread started, and
noted that expr matching provides the more flexible and potentially most
consistent [try] command, but is somewhat ugly around the actual match.
I had been letting this stew for a bit (paid-work time pressures) but it
looks like decisions need to be taken soon. Still, I prefer to take a
good look at all angles of a problem before taking decisions that become
permanent.
Regards,
Twylite
|