Re: 'catch' statement modifier
Luke ~ These matters are covered at some length in RFC 88 and Apocalypse 4. http://www.avrasoft.com/perl6/rfc88.htm http://www.perl.com/pub/a/2002/01/15/apo4.html Yours, &c, Tony Olekshy Luke Palmer wrote, at 2003-11-23 11:55: > > I was reading over some code that used the MIDI module, and saw the > C method. I began wondering, how does one report the > error if he feels like it, but let the module report the error if not, > in a concise way. > > What about something along the lines of a C statement modifier, > like: > > $opus.write_to_file($file) catch die "Couldn't write to $file: $!\n"; > > Which would be equivalent to: > > try { > $opus.write_to_file($file); > CATCH { > die "Couldn't write to $file: $!" > } > } > > It doesn't read quite as nicely as I'd like, but I think it could be a > very useful notation. After all, if I have to type a lot when I'm > handling errors, I'll prefer not to handle them at all. > > Which reminds me, if you throw an exception inside a CATCH block, does > it propogate outside to be caught by other CATCHes in the same block > that are lexically lower, or does it propogate outside of the enclosing > scope. That might be a little confusing... for example: > > try { > try { > do_something(); # Throws > CATCH { die "Foo" } > CATCH { die "Bar" } > } > CATCH { print "$!" } > } > > Does that print Foo or Bar? > > Luke > >
Some Apocalypse 4 exception handling questions.
In Apocalypse 4, Larry Wall wrote: | | In fact, a C of the form: | | CATCH { | when xxx { ... } # 1st case | when yyy { ... } # 2nd case | ... # other cases, maybe a default | } | |means something vaguely like: | | BEGIN { | %MY.catcher = { | given current_exception() -> $! { | | when xxx { ... } # 1st case from above | when yyy { ... } # 2nd case from above | ... # other cases, maybe a default | | die;# rethrow $! as implicit default | } | $!.markclean; # handled cleanly, in theory | } | } Beautiful. The synthesis of CATCH, BEGIN blocks, %MY, given, when, break, dwim =~, die, $!, $!.clean, and $!.stack is awe-inspiring. The way proto-exceptions, fail, and use fatal work together is also brilliant. I particularly enjoyed this one: CATCH { when @$! =~ Foo { ... } } I do have a few questions. 1. Does this example: { my $p = P.new; LAST { $p and $p.Done; } foo(); my $q = Q.new; LAST { $q and $q.Done; } ... } effectively get compiled into something like: { my $p; my $q; $p = P.new; LAST { $p and $p.Done; } foo(); $q = Q.new; LAST { $q and $q.Done; } ... } If not, how can we evaluate $q in the LAST block if foo() dies? Or are LASTs not handled by a magic BEGIN mechanism? Or are the LASTs converted into a BEGIN plus some run-time state variable that is only set when the LAST is encountered during execution? Or am I missing the point entirely ;-? 2. Consider the following example: for my $file ( @files ) { my $f is last { close } = open $file or next; foo($f); CATCH { default { print "foo($f) failed\n" } } } The last and CATCH blocks must be invoked at the end of each time around the for block, no? Or should I be writing: for my $file ( @files ) { try { my $f is last { close } = open $file or next; foo($f); CATCH { default { print "foo($f) failed\n" } } } } 3. Would the following execute the C? When do I have to worry about "accidentally" catching control exceptions? sub ... { return if 1; fragile(); CATCH { default { die "Couldn't fragile." } } } 4. The test for "block exited successfully" is C< !$! || $!.clean >, for the purposes of the block-end handing code, correct? So KEEP is like LAST { if ( !$! || $!.clean ) { ... } } and UNDO is like LAST { unless ( !$! || $!.clean ) { ... } } in which case CATCH is actually like UNDO with an implied given, die, and $!.markclean, except it's handled in a different end- block order, yes? 5. What is the order of processing all these special blocks at the end of their containing block? Is it: 1. CONTINUE 2. CATCH 3. KEEP 4. UNDO 5. LAST 6. POST or some other fixed order, or is there some sort of order-of- encounter interleaving of some of the kinds of blocks? 6. What is the value of my $x = try { "1" CATCH { default { "2" } } LAST { "3" } }; What happens for each permutation of replacing "n" by die "n"? 7. Is there any particular reason why multiple CATCH blocks can't simply be queued in some fashion like multiple LAST blocks? Yours, &c, Tony Olekshy
Re: Damian Conway's Exegesis 2
Felicitations. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Unexpected behavior.
ns to use exceptions for signalling errors. The one thing we don't want on this front in the design of Perl 6 is some half-baked concept of exception handling that (1) doesn't work well in production, and (2) prevents the development of a module-based mechanism that does work well. "All this talk about exceptions" is just work toward nailing down the structural details of the -language layer, to provide a reasonable working model of the community perspective to the good folks over at -internals. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Toward a hybrid approach.
Glenn Linderman wrote: > > Tony Olekshy wrote: > > > > If we take this approach then we know exactly what the following > > code will do. > > > > { my $p = P->new(); > > > > $p->foo and always { $p->bar }; > > > > except Error::IO { $p->baz }; > > } > > > > We also know when the block propagates unwinding; in particular, > > it doesn't if $p->foo raises an Error::IO exception, unless > > $p->baz throws, but it does if $p->foo raises some other > > exception (or if $p->foo doesn't raise an exception and $p->bar > > throws). > > By rule 2 above, it would seem that if $p->foo raises an Error:IO > exception, that the except block hasn't yet been seen, and > therefore the block should propagate unwinding. Ah, yup. ++$bugs{$self} > [In RFC 119] The except clause [...] was intended to catch the > exceptions of only the statement to which it was attached. I like > the conditional syntax you have added to the except clause, to > allow catching only specific exceptions. However, in making it a > standalone statement, the semantics change significantly. As a > clause, the except clause could be attached to the single > statement (or block) from which exceptions it was supposed to > handle might be propagated. As a statement, however, it seems to > have acquired a block level scope [...] I think I know what happened. Back in the 'Assign to magic name-of-function variable instead of "return"' thread, the discussion of the dangling always block came up again. John Porter pointed out that the difficulty of determining the scope of the operation is decreased if the always block is placed inside the block to which it applies, not after it (more below). I used that notion in proposing the hybrid, and inadvertenly broke some of the functionality of RFC 119's dangling except block. > The RFC 119 except clause, which was joined to the prior > statement, affected only that prior statement. In other words, > the except clause and always clause had different relationships > to the statement to which they were attached. I'm concerned that I may not understand what you mean by that. I see two alternatives for the dynamic behaviour of the except block, which I'd like to try to explain by considering their "try" dualities. Then perhaps you can let me know which you mean, or if I'm still missing something. Alternative 1: Start with the following code, as written by a developer: { a; b except { c }; d; } Assume, for the sake of discussion, that Perl 6 blocks have try-like semantics, as if the programmer had written: try { a; b except { c }; d; } Now, after statement a (if it did not throw) and *before* b (not after it, like an except statement would), Perl dynamically converts the except block into an equivalent catch clause and unshifts it onto the try statement's pending clause's list. From that run-time point on, it's as if we were executing: try { b; d; } catch { c }; Perl then continues executing statement b. CONSIDERATIONS: c is executed only if b or d throw. Unwinding is propagated if b or d throw, unless c does not throw. Alternative 2: The following code: { a; b except { c }; d; } Is the same as: { a; try { b } catch { c }; d; } CONSIDERATIONS: c is excecuted only if b throws. Unwinding is propagated if b throws, unless c does not throw. Unwinding is also propagated if d throws. So, given those considerations, is one of those alternatives the dual of your concept for the dynamic behaviour of the except block? Now on to the matter of the scope of the dangling block construct. I think I understand the "a except { b }" case: the (deferred) execution of b (iff unwinding) comes into effect just before executing a. In the case of: a and b except { c } Does the deferred execution of c iff unwinding come into effect just before a, or just before b (and only if a is true)? And in the case of: if ( a ) { b } else { c } except { d } Does the deferred execution of d iff unwinding come into effect just before the "if", or just before the "else" (and only if a is false)? Does this work: if ( a ) { b } except { c } else { d } except { e } For reference, here are all the scoping alternatives just discussed, using try's notation: try { a } catch { b } try { a and b } catch { c } a and try { b } catch { c } try { if ( a ) { b } else { c } } catch
Re: End-of-scope actions: do/eval duality.
Glenn Linderman wrote: > > Tony Olekshy wrote: > > > Traditionally Perl has had both the "do" and the "eval" block > > forms, the latter which traps, the former which doesn't. > > In the perl 5 pocket reference 3rd edition page 63, it claims that > $@ is set to the result of an eval or do. How does this impact > exception handling tests on $@ to determine if an exception was > thrown, if $@ can be set by a do ? OR is that an error in the > pocket guide? The following program $@ = 0; eval { 1 }; print '$@: ', $@, "\n"; $@ = 0; eval { die 2 }; print '$@: ', $@; $@ = 0; do { 3 }; print '$@: ', $@, "\n"; $@ = 0; do { die 4 }; print '$@: ', $@; which produces the following results $@: $@: 2 at ... line 2. $@: 0 4 at ... line 4. would suggest that - eval{} always touches $@, setting it to undef or some die's argument, and unwinding (if any) is terminated. It does not set $@ to the result of the eval. - do{} doesn't touch $@ unless the block throws, in which case $@ is some die's argument, and unwinding begins. It does not set $@ to the result of the do. If so, it would appear that the pocket reference has it wrong, and that the traditional operation of "do" is not affected by the details of any try/throw/except/always/catch/finally mechanism that specifies a particular behaviour for $@. From an exception handling perspective, "do" is transparent. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Toward a hybrid approach.
"David L. Nicol" wrote: > > Tony Olekshy wrote: > > > If we take this approach then when you just want to casually say > > > > my $f = open $file; always { close $f }; > > > > you can. I like that. In addition, [...] > > How about "later" instead of "always" > > Because: "later" is a time in the future, but we don't know exactly > when, > > and "always" has an aspect of continuing in time which is > distracting (to me.) The problem may be that a dynamic always statement means both "no matter what happens" and "not until later". The static finally clause just means "no matter what happened" (the effect is immediate). For the record, here is the set of keywords for various exception handling clauses that we culled from the errors mailing list, during the RFC process, for the Issues list in RFC 88: raise, always, onException, when, signal, fail, handle, otherwise, unwind, trap, quit, trip, deal, freak, panic, cope, punt, ascend, cough, sustain, overrule, and (of course) longjmp. (The authors were not recorded, to protect the guilty ;-) If we want to choose descriptive names, they should be something like this: except on_dynamic_exception_match always on_dynamic_end_of_block_scope catch on_static_exception_match finally on_static_end_of_block_scope or some other arbitrarily long concatenation of concept labels. In the end, we'll choose a more reasonable set of names, and quickly become used to them. I don't really care what they are, but see RFC 88 for some constraints on the properties they should satisfy. In particular, the keywords should not talk about "failure", because exceptions can in fact be used to signal "success" plus a "get me out of this mess" longjmp. As Piers Cawley wrote during the RFC discussions, "Ah, the 'Bloody hell it worked! That's exceptional' style of programming." ;-) Yours, &c, Tony Olekshy
End-of-scope actions: Core exceptions.
Nicholas Clark wrote: > > my $f = open $file or die "can't open $file"; > > is troublesome. It doesn't report *why* the file can't be opened. > > [...] *flexible* exceptions are needed The first version of RFC 88 didn't care what exception objects were, but discussions in the errors mailing list convinced us that we should define a base class for exceptions, to guarantee that certain things are there. Developers can of course define derived classes to extend what exceptions can know/do, and the RFC 88 syntax allows these properties to be used to write conditional catches and fancier unwinding traceback reports. An example of such a derived exception class is included in RFC 88. See the comments on your IO layer problem below. Jarkko Hietaniemi wrote: > > Nicholas Clark wrote: > > > > Jarkko would really like > > > > print "Foo\n"; > > > > in a void context to behave as > > > > print "Foo\n" or die $!; > > Not just basic I/O but anything 'system': pipe(), system(), > opendir(), mkdir(), chdir(), fork(), socket(), and so on. RFC 88 can be used as an exception handling mechanism independent of whether or not the core uses exceptions to signal errors (whether in system calls, math operations, or -gasp- the parser). If the core does use exceptions to signal errors (whether or not controlled by a pragma), then RFC 88 says it punts to RFC 80 on the matter of the details of the derived exception class to be used as the base for core exceptions. RFC 88 only requires that the core exceptions inherit from the base class. RFC 80 is free to define its own class hierarchy (things like Exception::CORE::IO::NotFound), and it's own rules for what information is contained in an exception's message, and how it is formatted. These message-building operations can be overridden for various sections of the core exception class hierarchy. RFC 88 shows a simple way to do pragma-based conversion of error modes at the core API (in which case no exception objects, in the OO sense, need be handled by the bulk of the core), and a simple way to support both exception-based and return-code-based error handling modes based on the dynamic value of a pragma, in module APIs. Personally, I think we should just use exceptions to signal errors, and then (1) there's no need for this pragma-based duality, and (2) you don't have to worry about forgetting to check return codes (thereby mis-handling failures). Nicholas Clark wrote: > > I'm experimenting with PerlIO layers. If a layer decides it can't > honour an open due to a reason not covered by errno, it has no way > of reporting this. If exceptions are being used to signal errors, your layer can simply stack an exception that is an instance of a class that inherits from, say, Exception::CORE::Layer, and those exceptions can track whatever sort of error-related data you want. Something like this: package MyLayer; sub OperationFoo { try { code that performs operations using layer below me } catch { throw Exception::CORE::Layer::Me::CantFoo "additional information based on my layer", other => "info", from => "me too", ...; } } Now, if things go bad in the layer below, when somebody gets around to telling the user about the unwinding, $@->show will include messages like the following: ... Exception::CORE::Layer::Me::CantFoo: additional information based on my layer. Exception::CORE::Layer::Low::CantBar: additional information from lower layer. ... You effectively have C< map { $_->{errno} } @@ > available for all the exceptions raised since the last time an exception was cleanly caught. Nicholas Clark wrote: > > I think that it would be nice in 5.8 to (optionally on some > pragma?) make print, close and a few others in void context croak. > It would actually make writing perl scripts easier. You'd know > when your disk became full (or you went over quota), albeit in > with a messy error. OK, script crashing with an uncaught > exception isn't nice, but it's nicer than silently losing data > IMHO. RFC 88 says, in support of the notion of using exeptions to signal errors, "How many of us check for IO failures after prints? And if you're writing a simple program you wouldn't want to have to, but you would want the program to shut down after a failure if you don't check." Also, since you're not returning error codes any more, the matter of the void context is moot: failures always throw. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Background.
Peter Scott wrote: > > > try { > > die "foo"; > > } catch { > > die "bar"; > > } > > > > [...] > > Surely the first one catches it cleanly since it has a > "catch-all" catch clause. That "catch-all" clause throws. In RFC 88 we said, in the Definitions section, Cleanly caught This means the trapping and handling of an exception did not itself raise an exception. So the first one is caught, but it's not cleanly caught. What's the difference? Consider try { ... } catch { foo() }. If foo() throws then try's exception isn't cleanly caught, so unwinding *is* propagated at the end of the try statement. If foo() doesn't throw the exception is cleanly caught, so unwinding is *not* propagated at the end of the try statement. Consider $r = try { $x / $y } catch { $x / $z }; f(); If $y == 0 you want to use $x / $z for $r and then call f(). But wait, *unless* $z == 0 too, in which case you want to unwind, no? So, we propagate after an exception if it is not "cleanly" caught. Just catching it is not enough to terminate propagation, the catching has itself to be "clean", otherwise there's an exception in the catch that hasn't been caught. There's a failure in the failure handling. You want to report that, not catch it. (And if you do want to catch it, just add clauses.) Yours, &c, Tony Olekshy
Re: End-of-scope actions: Garbage collection.
Dan Sugalski wrote: > > Tony Olekshy wrote: > > > >I think we need to provide some way for developers to explicitly > >specify predictable end-of-block cleanup (using something like an > >always block or finally clause). > > Attributes or other things stuck on the end of blocks strikes me as > a messy sort of way to do this. Why not something simple like > GC::cleanup or GC::collect, to explicitly cleaning up after > destructable objects and collect garbage memory respectively? Hmm, I think we still have a terminology impedence mismatch here. It's probably my fault. When I said "end-of-block cleanup", I didn't just mean DESTROY and/or GC. Consider the following: { my $p = P->new; try { $p->foo } finally { $p->bar }; $p->baz; } There's an explicit end-of-block "cleanup" operation for the try block, namely $p->bar, which is called whether or not $p->foo throws. Then, iff neither $p->foo nor $p->bar throws, $p->baz is called. Then (whether we're now unwinding or not) there's an end-of-block cleanup operation for the anonymous block, but this an implicit end of lexical scope cleanup for $p (not an explicit finally clause or POST block), so if $p's object's refcount is 1, or it is found in some sort of sweep, then DESTROY and garbage collection can be considered. Meanwhile, I agree that try/finally (or any similar such explicit exception handling mechanism) is not an appropriate way to talk about GC, more strongly, I think the two mechanisms should be explictly decoupled. (Personally, I don't think developers should expect implict cleanup to be guaranteed to happen immediately, and I don't think the Perl 6 designers should be so constrained, but that's probably just because I've structured my code so as to obviate the need, and therefore don't appreciate it.) I only mentioned try/finally in the GC thread to note that if we use a mechanism like that to handle explicit end-of-scope problems, then you guys are free to do whatever you want with DESTROY and GC, without having to worry about explicit end-of-scope stuff. You can design any high-quality scheme you want, and for those cases where an explicit alternative is required, try/finally (or equivalent) can be used. I was trying to make your problem simpler, by punting one part of it to the results of the try/finally effort. Sorry for the confusion. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Background.
Branden wrote: > > There's something I didn't quite understand about RFC 88: > > When I > > try { > die "foo"; > } catch { > die "bar"; > } > > I die with "bar", right? But what happens if I > > try { > die "foo"; > } finally { > die "bar"; > } > > I die with "foo" or "bar"? Why is this the right behaviour? Any > sample code that shows why this should be done this way and not > the other? I both cases, since the "foo" exception is not cleanly caught by either example, unwinding will be propagated at the end of the try statement. The code in catch and finally clauses of any outer try clause (one "containing" the above examples, but not necessary in the same lexical scope) will see the following: $@[0] eq "bar" and $@[1] eq "foo" and $@ = $@[0] with the proviso that the items in the @@ list are actually exception objects that stringify back to the message passed to die. In other words, RFC 88 maintains in @@ a stack of all exceptions raised while in the process of handling and propagating unwinding, with new exceptions unshifted to occupy $@[0] when they occur. This stack is only cleared when exception handling cleanly catches (that is, a catch clause matches and completes without itself raising an exception). Two useful results arise from this technique. 1) In a catch block, print join("\n", @@), "\n"; effectively generates an exception stack traceback. In fact, RFC 88 allows you to say $@->show with that effect, and supports options for including things like showing the Perl stack traceback as at the raising of the first exception (the one in $@[-1]), showing exeption class names, and/or showing other debug information only to developers and log files, but not to end users. That's how it generates messages like this UIM.1234: Can't add a new person to the database. APP.2345: Can't update Company relationship. DBM.3456: Trouble processing SQL UPDATE clause. DBM.4567: Unable to write to Locations table. IOM.5678: Can't open file ".../locations.ndx". IOM.6789: File ".../locations.ndx" not found. which I referred to in http:[EMAIL PROTECTED]/msg05799.html 2) In the test expression in a conditional catch clause, you can operate on the entire @@ stack. For example, try { ... } catch grep { $_->isa("Foo") } @@ => { bar(); } will call bar() only if the try block unwinds *and* any of the exceptions involved in the unwinding are instances of a class that inherits from Foo. Similarly, the following two clauses catch grep { ref $_ =~ /Alpha/ } @@ => { ... } and catch grep { $_ =~ /Beta/ } @@ => { ... } match only if any exception class name matches Alpha, or any exception's message string matches /Beta/, respectively. You can also test other exception object properties with tests of the form catch grep { $_->{tag} eq "XXX.1234" } @@ => { ... } Of course, if you're only interested in the most recent exception, skip the grep operations in these examples and just test $@ directly (which works because of the rule that $@ is always equal to $@[0]). Both of the above results are implemented in the RFC 88 Perl 5 reference implementation (modulo syntax). There are more examples at http://www.avrasoft.com/perl6/rfc88.htm#Examples Yours, &c, Tony Olekshy
End-of-scope actions: Toward a hybrid approach.
I've been thinking about the effect of the minimalist changes I made to the RFC 88 reference implementation, and I don't see any good reason not to support both the static and the dynamic forms of end-of-block-scope actions. Consider the following proposal. 1. Support a try statement (a modified eval) which can have catch and finally clauses, using the semantics of RFC 88. If you want to use catch or finally (instead of always or except) you have to use try. These static catch and finally clauses come into consideration as at when the try is seen, independent of what happens in the block. 2. Support always and except blocks. These constructs may be used without requiring a try before the block. They are dynamic operations which only come into play when they are encountered in the block, in run-time order. 3. Implement always and except by converting them into equivalent dynamically-created catch and finally clauses in the block's actual or implied try statement. Then the semantics of always and except, the details of their effect on exception propagation, and the details of conditional except blocks, are already defined (by RFC 88). If you only want to use always and/or except that's fine, you don't need the try. If you don't want to use conditional catches and/or excepts you don't have to. We get the advantage of TMTOWTDI, while keeping the details understandable and relatively simple to implement by only using one set of semantic rules to cover both cases. If we take this approach then we know exactly what the following code will do. { my $p = P->new(); $p->foo and always { $p->bar }; except Error::IO { $p->baz }; } We also know when the block propagates unwinding; in particular, it doesn't if $p->foo raises an Error::IO exception, unless $p->baz throws, but it does if $p->foo raises some other exception (or if $p->foo doesn't raise an exception and $p->bar throws). If we take this approach then when you just want to casually say my $f = open $file; always { close $f }; you can. I like that. In addition, when you want to carefully say my $result; foreach ... { try { my $p = Framework::P->new( ... ); my $q = Framework::Q->new( ... ); $result = Server::Foo( $p, $q, ... ); } catch Exception::Framework::Bar { $result = Client::Fallback( $p, $q, ... ); } finally { $p and $p->Done(); } finally { $q and $q->Done(); } last if $result; } you can. I like that too. Easy easy, hard possible. The more I think about having both these options available, the better I like it. Both allow me to be lazier (the latter in the case where the exception handling logic is complicated enough that doing it carefully is actually lazier). I get the impression some people think I want verbose code, or some sort of impractial so-called "ivory tower" solution, but I'm really just as lazy as you (probably lazier, but we don't want to debate that here ;-) Yours, &c, Tony Olekshy
Re: End-of-scope actions: POST blocks.
"David L. Nicol" wrote: > > POST{stuff} is a macro for > > push (my) @Deferred_stuff, sub {stuff}; # my on first use in a space Since the reference implementation requires try, @Deferred_stuff is actually try's argument list (a bunch of tagged catch and finally blocks). The "my" is provided by try. But in general, yes, if post/always/except is part of Perl 6 then every block will have to keep track of whether or not any such action has been seen during the excecution of the block. Fortunately, it need do nothing special if no such actions have been seen (such as, oh, GC for example). > and return(arg) becomes, including implied EndOfBlock return: > > &{shift @Deferred_stuff} while @Deferred_stuff; return(arg) Yes, but you have to keep track of exceptions raised while executing &{shift @Deferred_stuff}, so you can propagate them if they occur, and you have to keep track of catch clauses that don't raise exceptions, because they can terminate propagation. For example: my $r = do { except { h() }; f() }; g(); or my $r = try { f() } catch { h() }; g(); should set $r to f() unless f() raises an exception, in which case it should use h() for $r, and whether or not f() raises an exception it should *not* propagate such an exception, it should execute g(). Unless, of course, h() throws, in which case it should propagate that exception, not set $r, and not call g(). On the other hand: my $r = do { always { h() }; f() }; g(); or my $r = try { f() } finally { h() }; g(); should call h() whether or not f() throws; and if f() or h() throw the exception should be propagated, otherwise $r should be set, and g() should be called. Yours, &c, Tony Olekshy
Re: End-of-scope actions: POST blocks.
great discussion about this, with one camp suggesting that post/finally shouldn't propagate trapped exceptions, because you can always do it explicitly in every post/finally you write. Others considered that to be a dangerous proposal, because of how easy it would be to forget the re-throw in the common case. The approach taken by RFC 88 was to work out a syntax and semantics for multiple conditional catch clauses that still makes the easy easy the helps make the hard possible. In the updated reference implementation, I dynamically convert "except"s into "catch"s, which seems to work, so far. Yours, &c, Tony Olekshy
Re: End-of-scope actions: Garbage collection.
Dan Sugalski wrote: > > [...] I wasn't talking about try{}/finally{} stuff. I was talking > about DESTROY (or its equivalent) for objects, which unfortunately > can't be tied to any one particular place in the code. and, from another thread: > I really don't want to guarantee predictable end-of-block cleanup, > though, since that means a potentially expensive GC run more often > than we might otherwise do. Yes, and I agree with you. I'm just saying that if we're not going to automatically guarantee predictable end-of-block cleanup, then I think we need to provide some way for developers to explicitly specify predictable end-of-block cleanup (using something like an always block or finally clause). Since this explicit operation has to deal with stack unwinding, and therefore with exception trapping, this end-of-block cleanup operation has some eval-like semantics. Therefore, a complete consideration of the matter of end-of-scope includes not just (1) garbage collection, and (2) DESTROY, but also (3) the matter of end-of-scope operations explicitly requested by the developer, in an explicit order which may or may not be related to GC or DESTROY order, and (4) the matter of the interaction between explicit end-of-scope operations and explicit conditional exception handling. Jan Dubois wrote: > > Could you guys please use "destruction" or "cleanup" as the term > for the end-of-scope processing (see e.g. C++). Finalization is > used everywhere else to mean: called by GC before the memory is > released (see e.g Java/C#). Thanks for the reminder about the ever-present problem of conflicting terminology. I was using "finalization" as a generic label for various kinds of end-of-scope operations. I'm not doing it any more ;-) I think there are three concepts we need labels for, in order to keep the discussion clear. - Code automatically invoked just before an object is finally garbage collected, whenever that actually happens. - Code automatically invoked when an object is finally DESTROYed, whenever that actually happens. - Code automatically invoked when a block scope finally ends, whether because of local flow-control or because of stack unwinding. These are all "end-of-scope" considerations, for various values of "scope". They're all about answering the following question: When the closing curly brace in { ...; my $p = P->new(); ... } is encountered, what happens to the object referred by $p? Yours, &c, Tony Olekshy
End-of-scope actions: Garbage collection.
Dan Sugalski wrote: > > I do wish people would get garbage collection and finalization split in > their minds. They are two separate things which can, and will, be dealt > with separately. > > For the record: > > THE GARBAGE COLLECTOR WILL HAVE NOTHING TO DO WITH FINALIZATION, AND > NO PERL OBJECT CODE WILL BE CALLED FOR VARIABLES UNDERGOING GARBAGE > COLLECTION. > > Thank you. Thank *you* Dan. I was beginning to wonder if anyone understood. For the record now, GC is none of your business, you're not allowed to know how it works (it's a "black box"). Finalization is handled by try {} finally {}. Finally. Finalization. Get it? Yours, &c, Tony Olekshy
End-of-scope actions: do/eval duality.
John Porter wrote: > > There is no try, there is only do. :-) Nonsense. Traditionally Perl has had both the "do" and the "eval" block forms, the latter which traps, the former which doesn't. "try" is just a slightly souped-up "eval" that better handles the class of problems introduced when exceptions are more commonly used for error handling (by adding some well-defined auxiliary clauses to the statement). Perl has always used an in-flow statement (namely "eval") to signal an unwind-trapping context; are you *sure* you want to change that tradition now? If we work together on this we can make Perl 6's exception handling something worth having worked on. If we throw a bunch of untested ideas together we can only hope they work (at least I hope they work, since Perl has been my favourite language for the last twelve years). Now, shall we? Yours, &c, Tony Olekshy
End-of-scope actions: Error messages.
Johan Vromans wrote: > > [...] As a result, error messages become utterly useless. I almost > never see a Java program that reports "Cannot open file foo". > Instead, it reports a java.lang.ioerrorexception and a stracktrace > of several pages. Useless if you do not have the source, often > even if you have. RFC 88 shows how to solve this problem via its built-in exception unwinding stack, which allows to you generate messages like this when the exception is punted to the user: UIM.1234: Can't add a new person to the database. APP.2345: Can't update Company relationship. DBM.3456: Trouble processing SQL UPDATE clause. DBM.4567: Unable to write to Locations table. IOM.5678: Can't open file ".../locations.ndx". IOM.6789: File ".../locations.ndx" not found. You can also display (or not) the stack trace (there's actually more than one stack trace involved during unwinding, but that's a detail), depending on whether or not the user has developer access (say) or not, and include the exception stack and Perl traceback in the system logs (or not), and include stack traceback and *other* information that should not be presented to the user but should be presented to the developer and the logs (or not), all as appropriate to *your* application. Might that help? Yours, &c, Tony Olekshy
End-of-scope actions: Reference model 2.0.2.1.
I have extended the RFC 88 Perl 5 reference implementation to support rudimentary POST and CATCH blocks, for which I've used "always" and "except" as the keywords. The new version is http://www.avrasoft.com/perl6/try6-2021.txt Save that file as Try.pm and perl -we "use Try regress => 1" to run the regression tests. All I've done so far is export "always" and "except" subroutines that splice equivalent "finally" and "catch" arguments into try's argument list. This means that, for now, always and except only work in try blocks, and their error messages are reported as those of the generated finally and catch blocks. However, as a result, the following regression tests do work: !always-1 Always stacks a finally. try sub { print "1\n"; always sub { print "3\n"; }; print "2\n"; }; =expect 1 2 3 !except-1 Except stacks a catch. try sub { print "1\n"; except sub { print "2\n"; }; die; print "BAD\n"; }; =expect 1 2 And, it does mean that we have a defined semantics for the purpose of discussion, upon which perhaps we can derive the detailed semantics for Perl 6's exception handling mechanism. Please bear with me. The purpose of this exercise is to make Perl 6 better, not to make it into my version of Java. In fact, I've never programmed in Java. Thank you very much. I learned to appreciate the beauty and elegance of structured exception handling in Scheme and Object Pascal (one of which is a B&D language, one of which is not). I'm just trying to figure out how I might be able to make my miniscule contribution to Perl 6 by writing the exception handling FAQ. When I'm explaining { f() always { g() except Error::IO { h() } } } I need to know: does h() get called if f() raised an Error::IO or only if g() does? Yours, &c, Tony Olekshy
End-of-scope actions: POST blocks.
Nicholas Clark wrote: > > It makes them far more useful as tidy up things if they are tacked > on at runtime, not compile time. If I understand, it is proposed that code like this: { Alpha; POST { Beta }; Gamma; POST { Delta }; Epsilon; } will behave something like this: { my @onEnd = (); try { Alpha; unshift @onEnd, sub { Beta }; Gamma; unshift @onEnd, sub { Delta }; Epsilon; } finally { foreach (@onEnd) { &{$_}(); } } } If so, then I have some observations. - It does have in-flow presence, so it doesn't suffer from the problem that "always" has; POST is a statement, not a dangling clause. That fixes my main complaint with RFC 119. On the other hand, now there's nothing at the end of the scope to tell you whether or not have to revisit the whole scope to check if there are any POST clauses before you advance your mind to the next statement. Hmm. - It does solve the dual free problem. Where RFC 88 says: try { my $p = P->new; my $q = Q->new; ... } finally { $p and $p->Done; } finally { $q and $q->Done; } the proposed method could say: my $p = P->new; POST { $p->Done; }; my $q = Q->new; POST { $q->Done; }; ... On the other hand, there is no easy way to specify the order in which $p->Done and $q->Done are invoked, independent of the order in which $p and $q are constructed. Hmm. - The semantics aren't quite the same as "try", which declares exception handling blocks that are active once try's block is entered, not when the declarations are passed in the block. Perhaps both mechanisms should be supported? RFC 88 does suggest that RFC 119 should be considered independent of RFC 88, not as an alternative thereto. - If the contents of a POST block raises an exception, how does it affect other POST blocks? In the example above, if $p->Done throws, then $q->Done should be called, *and* vice versa, *and* unwinding should propagate if either one threw (or anything else did for that matter, unless it was caught without the catching throwing). - What about CATCH blocks (what RFC 88 calls "catch" and RFC 119 calls "except")? What exactly is the interaction between these dynamic POST and CATCH blocks? We will need a detailed semantics, along the lines of http://www.avrasoft.com/perl6/rfc88.htm#Unwinding_Semantics - What about conditional CATCH blocks? What syntax can we use that interacts reasonably well with the rest of Perl? - What's the return value? With RFC 88 you can say: my $r = try { f() } catch { 0 }; What are the syntax and semantics in the CATCH/POST case? Perhaps something like: my $r = do { CATCH { 0 } f() }; Hmm. Yours, &c, Tony Olekshy
End-of-scope actions: Visibility.
John Porter wrote: > > Tony Olekshy wrote: > > > > I think "always" should be part of an explicit statement, such > > as "try", not some implied property of block structure introduced > > by a dangling clause. > > Why? There's an old engineering joke about instructions that go on and on for pages about how to attach the cover with the special bolts and torque them just so and get how to do the x-ray tests and sign off the job, and somewhere near the end there's a note that says, "before attaching the cover be sure to test the humdinger transpondence." Which of the following sets of instructions is better? These: 1. Skip step one. 2. Kill yourself. 3. Note: if step one was skipped then skip step two. Or these: 1. Warning: if step two is skipped then skip step three. 2. Skip step two. 3. Kill yourself. When instructions are written which modify the way other instructions are to be carried out, the modifying instructions should appear before the modified instructions. For humans reading source code that's "before" in source code order, for CPUs it's in instruction sequence order. That's what "eval" and "try" do. They tell you up front, "Warning, if the following block unwinds, do not propagate your mind out of here as you would normally, instead examine the source code following this block, because trapping is now in effect for the scope of the block." Trapping is special; it's not like traditional flow control because it does not involve any in-flow visible source code signal which indicates to the reader that the implied goto-on-unwind is in effect, unless we explicitly require something like eval or try. It's not like a dangling continue block, because such blocks are triggered by an in-flow visible statement like next. That's why [RFC88] quotes [ESE-1994] to the effect that, "Among the features offered by programming languages to support exception handling are (1) The ability to distinguish the normal control flow from the exception handling flow to make the structure of the program clear [...] Most early programming languages do not provide specific features for exception handling, but rather use the normal constructs to implement it. [...] Obviously this and other ad hoc methods do not satisfy the requirements listed above." On the other hand, a post-hoc "always" says, "Note: if you've already propagated your mind out of here, and you aren't reading this code, you shouldn't have." It's like a roadway sign that says, "The section of road you have just finished driving on may have been slippery if it was wet." It's correct. It's do-able. It's a poor way to do it. Yours, &c, Tony Olekshy ESE-1994: The Encyclopedia of Software Engineering, J.J. Marciniak (editor), John Wiley & Sons Inc, 1994. ISBN 0-471-54002-1 RFC 88: Omnibus Structured Exception/Error Handling Mechanism, http://www.avrasoft.com/perl6/rfc88.htm
End-of-scope actions: Background.
Tony Olekshy wrote: > > Damian Conway wrote: > > > > Actually, I do agree that Perl 6 ought to provide a universal > > "destructor" mechanism on *any* block. For historical reasons, I > > suppose it should be C, though I would much prefer a > > more generic name, such as C. > > Perl 6 ought to provide universal exit mechanisms for any delimited > scope, both for the case of normal sequential termination of scope, > and for the typically special cases of termination by non-local > flow control due to stack unwinding caused by the raising of an > exception. Hi, it's me again, the guy who won't shut up about exception handling. I'm trying, in this message and others under this subject prefix, to collect these end-of-scope matters into a unified discussion that can help us maximize the signal to noise ratio when dealing with *both* unwind trapping and end-of-scope situations during our coding efforts. This should also make it easier for those who are uninterested in the topic to avoid the discussion. Consider the following two cases: my $f = open $file with_end_of_scope { close $f }; my $y = f($x) but_if_that_unwinds_use { 0 }; These seem simple enough, so what could go wrong? Consider the following (where Done() is something that should be done in end-of-scope order, rather than in $p's GC order): my $p = P->new() with_end_of_scope { $p->Done } So far, so good. Now what about: { my $p = P->new() with_end_of_scope { $p->Done }; my $q = Q->new() with_end_of_scope { $q->Done }; ... } Let's see, what should happen after the ...? Well, $p->Done should be called, even if the ... started stack-unwinding flow-control. Maybe $q->Done before $p->Done, but that's a detail. And then $q->Done should be called, even if ... or $p->Done starts unwinding. And if ... or $p->Done or $q->Done starts unwinding, then the unwinding should be propagated as at the closing curly-brace, otherwise execution should continue with the next statement in local flow-control order. Oops, things just got a little more complicated. Now before we go any further, a minimalist implementation of all this is understood; it's called "eval" in Perl 5. As explained in RFC 88: try { may_throw_1 } catch may_throw_2 => { may_throw_3 } finally { may_throw_4 } can be written in Perl 5 like this: eval { may_throw_1 }; my $exception = $@; if ($exception) { my $test = eval { may_throw_2 }; $@ and $exception = $@; if ( ! $@ and $test ) { eval { may_throw_3 }; $exception = $@; } } eval { may_throw_4 }; ($exception ||= $@) and die $exception; but the latter solution has a lower signal to noise ratio, it scales poorly, and it doesn't implement any sort of exception unwinding stack concept. Right, so returning to our $p->Done plus $q->Done example, consider the case where we want to treat specially one of the possible exceptions that the ... could be unwinding due to. I'm switching to RFC 88 notation here, because if nothing else it is pedantic enough to make my intent relatively clear (hmm). Something like: my $result; foreach ... { try { my $p = Framework::P->new( ... ); my $q = Framework::Q->new( ... ); $result = Server::Foo( $p, $q, ... ); } catch Exception::Framework::Bar { $result = Client::Fallback( $p, $q, ... ); } finally { $p and $p->Done(); } finally { $q and $q->Done(); } last if $result; } What happens if Server::Foo throws an exception and starts unwinding? What happens if Client::Fallback does? What happens if Q->new() throws an Exception::Framework::Bar exception and Client::Fallback does *not* throw an exception? Under what circumstances does the end of scope propagate an exception (continue unwinding), and under what circumstances does it continue with the next statement in local flow-control order? What happens if Client::Fallback can't open file "Sigma"? Will the exception with the message about Sigma get back to the user, or will only the most recent message get there (one from $p->Done, perhaps)? By now people are asking, if this is so complicated, why not just keep it simple? For one thing, it is already the case that the RFC 88 reference implementation, in Perl 5, does keep the easy things easy. The first two examples in this message work as expected (modulo syntax), And then, once you look at the effect of a reasonable set of semantics, you find that less simple constructs do provide usefull functionality, often helping make the hard things possible (like really classy er
Re: assign to magic name-of-function variable instead of "return"
John Porter wrote: > > [EMAIL PROTECTED] wrote: > > > > Hmmm. If there's such an "always" block, I'd like to see it on > > all blocks, including the continue [1]. But then, it becomes > > hard to figure out to which block the always belongs > > That's precisely why these things should be shoved inside rather > than dangling off the end. JMHO. I think "always" should be part of an explicit statement, such as "try", not some implied property of block structure introduced by a dangling clause (inside or outside). Once you have an always clause, it has to be invoked during stack unwinding caused by the raising of an exception. This means there is an implied goto-on-exception pending throughout the scope affected by the always clause. This is not like if/else, for, or while, which are all marked up front, and only have explicit variant flow control. It is like eval, but note that eval is marked up front too. Like eval, the beginning of the scope for non-local flow control (such as always and catch) should be explicitly delimited, typically by using a keyword like try. Consider the following two cases: foreach ... { try { ... } catch { ... } finally { ... } } try { foreach ... { ... } } catch { ... } finally { ... } now take out the statement keyword and use magic dangling clauses: foreach ... { { ... } catch { ... } finally { ... } } { foreach ... { ... } } catch { ... } finally { ... } The signal to noise ratio has gone down, no? In fact, the cross product of these cases and the alternatives for dangling always block placement produce these four cases: foreach ... { ... catch { ... } always { ... } } { foreach ... { ... } catch { ... } always { ... } } foreach ... { ... } catch { ... } always { ... } { foreach ... { ... } } catch { ... } always { ... } Now play the problem backwards. Say you run into one of the cross-product constructs in some code. Is it clear to you what the scope and semantics are? Is it clear when always applies to the foreach block, and when it applies to the catch block, and when it applies to the foreach statement? What about the try/finally cases? It's pretty clear, IMHO, that the catch and finally clauses apply to the try statement, simply because try is a statement of which they are part. At that point, the body of the try block and any previous catch or finally blocks that are part of the same try statement are apparent. The previous blocks are critical, because under various circumstances blocks need to be triggered by exceptions raised in previous blocks. >From a psychology of programming languages perspective, wrapping the whole mechanism up into a statement per se provides the foundation upon which we can attempt to avoid the conceptual disaster produced by dangling clauses. Mixing up traditional sequential flow-control constructs and non-local stack-unwinding flow-control constructs, without clearly delimiting what you're doing, is (I think) a less than optimal idea. Remember that one of the main uses for catch and always clauses is error handling (as in, close file if opened even if error during processing thereof). I don't like language constructs that obfuscate my attempts to get error handling right (such as they are) because errors in error handling tend to make my code behave relatively poorly. Yours, &c, Tony Olekshy PS: since we're completely off subject, can we continue this under http:[EMAIL PROTECTED]/msg05604.html
End-of-scope actions, redux.
Damian Conway wrote: > > Actually, I do agree that Perl 6 ought to provide a universal > "destructor" mechanism on *any* block. For historical reasons, I > suppose it should be C, though I would much prefer a > more generic name, such as C. Perl 6 ought to provide universal exit mechanisms for any delimited scope, both for the case of normal sequential termination of scope, and for the typically special cases of termination by non-local flow control due to stack unwinding caused by the raising of an exception. Once you have both types of exit mechanisms, getting the syntax and semantics relatively complete and consistent is not necessarily trivial. Many people put a lot of effort into this very problem in the so-called errors mailing list during the Perl 6 RFC process. One version of the results, with a fairly complete description of the matters of dissent that were raised thereto, is contained in RFC 88, "Omnibus Structured Exception/Error Handling Mechanism", which I co-authored. Using RFC 88's notation, the example being discussed in the parent of this thread would be written something like this: sub read_it { try { my $fh = open @_; <$fh> } finally { $fh and close $fh } } The existence of the "try" keyword, the choice of other names, and actually important matters such as the lexical scope of $fh, are discussed in RFC 88. More complex constructs that are natural extensions of the concept are also considered, such as the very useful case of multiple finally blocks, along these lines: try { my $p = P->new; my $q = Q->new; ... } finally { $p and $p->Done; } finally { $q and $q->Done; } which invokes $q->Done if $q was allocated, even if $p->Done raises an exception! Please do consider RFC 88 before re-executing the entire debate on this matter again. While many ideas sound good in isolation, gluing the collection of ideas into a coherent whole can be more challenging. And, Larry did mention the try catch catch catch finally construct in his ALS talk. For your reference, here are some RFC 88 links: RFC 88 as HTML http://www.avrasoft.com/perl6/rfc88.htm RFC 88 as Text http://www.avrasoft.com/perl6/rfc88.txt RFC 88 as PODhttp://www.avrasoft.com/perl6/rfc88-pod.txt Perl 5 Try.pmhttp://www.avrasoft.com/perl6/try6-ref5.txt Regression Test http://www.avrasoft.com/perl6/try-tests.htm Yours, &c, Tony Olekshy
Re: Exception handling [Was: Re: Things to remove]
Glenn Linderman wrote: > > Just to point out that fatal is, indeed, as several people keep > saying, truly in the eye of the catcher. > > That said, none of the currently proposed mechanisms permit > "resume from fault" semantics, much less "resume from hardware > fault" semantics. Sounds like good RFC fodder to me! Hi, it's me again. Not to be a pain, but RFC 88 does say: retry There has been some discussion on perl6-language-error about the concept of re-entering try blocks on catch, and the possibility of using such a mechanism to replace AUTOLOAD. The author is of the opinion that in order to do this sort of thing properly one should use continuations, which are being discussed elsewhere to this RFC. The intent of this RFC is to provide a simple yet robust exception handling mechanism that is suitable for error handling, not for replacing AUTOLOAD. s/retry/resume/g I'll try to make that more clear in 88v3d1. Yours, &c, Tony Olekshy
Re: Exception handling [Was: Re: Things to remove]
Dan Sugalski wrote: > > Markus Peter wrote: > > > There is no such thing as an ultimately fatal error - it should > > always be up to the user of a module wether the program should > > die, but I guess you see that the same and will answer me with > > "use eval" then ;-) > > I hope you're speaking from a perl level--a segfault pretty much > spells "Game Over"... Dan, FYI ~ Yes, from a Perl level. In general, discussion on -errors have assumed that there will be a class of what we are generically referring to as exceptions that will indeed not behave according to the rules for all other exceptions (such as being able to be caught by eval { ... } ). Certainly segfault and hcffault (that's halt-and-catch-fire fault) would we in that class of exceptions. Other exceptions, such as out-of-memory, might almost be in that class, except when the emergency out-of-memory memory pool comes into play. I haven't though that one through. Then there's exceptions like divide-by-zero and (if C is in scope) cant-open-file. Clearly, these are catchable. There is currently a running dialogue on whether or not divide-by-zero and other such so-called "fatal" errors should be handled by a separate mechanism from that used for so-called "non-fatal" errors like cant-open-file, where is where you stepped in. Said dialogue will play itself out. Yours, &c, Tony Olekshy
Re: RFC thoughts and guidelines
On this matter, should something like this be a (meta) RFC? =head1 TITLE Guidelines for Developing Changes for Perl 6 (v0.1). =head1 ABSTRACT - If Perl 5 functionality is not broken (at least for some uses), then don't change it unless a critical required core change imples the change in functionality. - If other canonical functionality can be agreed on, add that in a fashion compatible with the existing functionality. - If additional functionality can be done in a (core) module, do it that way unless you have a *really* good argument otherwise. - If you can't do it in a module, try to abstract out the minimal changes to Perl (such as the addition of a well-defined hook) that will allow it to be done in a module. - Make discipline easier to enforce, but don't force any single model of discipline, which may not be appropriate to some uses of Perl. =head1 DESCRIPTION Case Study 1 Even if we agree on a canonical way to handle "error" exceptions (those generated by some sort of "failure" in the program flow), our model may not be suitable for other uses the core functionality provided by eval {}, die, and $@. It's nice to be able to do those other kinds of programs in Perl, too. Case Study 2 When a topic such as Perl's meta-object protocol generates so much heat, perhaps it's best not to over-constrain available solutions. Again, even if we largely agree on a new OO way, our model may not be suitable for other uses the core functionality provided by bless, the -> operator, and an extensible meta-object protocol. It's nice to be able to do those other kinds of programs in Perl, too. =head1 IMPLEMENTATION - Enhance the ability to use a common set of pragmas and modules across a group of files, so people can't say all those "uses" are an argument against doing things in modules. - Enhance performance of modules, so people can't use that as an argument against doing things in modules. - Add more hooks to Perl and enhance performance of function and method calls to allow modules to effectively implement additional low-level behaviour. Yours, &c, Tony Olekshy
Re: English language basis for "throw"
"Stephen P. Potter" wrote: > > I think fail() and handle() are good. Something fail()ed and > it was handle()d by an exception. Fail is no good, because exceptions can be used to indicate success. Just because you don't isn't a counter-argument. Exceptions are *not* the same as errors, that's just one semantic mapping. Consider the following case. You've got some complicated algorithm that doesn't normally succeed in finding an answer. When it does find an answer, it can be all over the place, but no matter what, you want to stop the algorithm and return the answer. When you succeed, you want to say throw Exception::MySuccess object => $answer_object; not fail Exception::MySuccess object => $answer_object; and then invoke the whole algorithm under: my $answer; try { my_algorithm; } catch Exception::MySuccess { $answer = $@->object; } If my_algorithm finds the answer then $answer contains the answer, otherwise $answer is undef. If my_algorithm throws anything but an Exception::MySuccess (such as an otherwise uncaught internal Perl error), the statement after the catch is not executed at all. That's why all the other words with negative connotation are bad choices for throw. It's up to the catcher to determine what's good and what's bad, not the thrower. The "classical" uses of try/throw/catch/finally have been around for many years. During that time, many languages have incorported the concepts, and after much heat and little light, the terminology. Could it be that's because, *all* things considered, it is good terminology? Consider "finally" vs. "always". Always? Even if force majeur? Finally simply means, "as the final act of the unwind processing". By the way, this discussion has moved to perl-language-errors, so the good folks here at perl-language-flow can concentrate on finding silly words for other Perl flow-control constructs ;-) Yours, &c, Tony Olekshy
Re: RFC 92 (v1) Extensible Meta-Object Protocol
"Randal L. Schwartz" wrote: > > Tony Olekshy wrote: > > > > Perl should be modified so that if C<$ISA::Search> (or equivalent) > > Do you mean "$YOUR_PACKAGE::ISA::Search" > which is in the package "YOUR_PACKAGE::ISA"? > > This would be the first time (to my knowledge) that something would > be in an unrelated package. Remember... there is no necessary > correlation between $A and $A::B as of yet. Your proposal would > breach that. Oops, sorry. That's what we do (aka get away with) in our little generator module, but I can see that it doesn't scale well. > Presuming the rest of the idea is deemed sound enough to implement > (:-), I'd strongly suggest the variable name be something like > $ISA_SEARCH or something like that, to keep it in the same package. Hmm, what about %ISA{Search}? Then, if we identify other meta-object protocol hooks (or parameters) we could specify them via other entries in %ISA. Meanwhile, @ISA could stay the same. I'm not too worried about the details at this point. However, what I would like is for a subset of Perl 6 to almost indistinct from Perl 5: additions are ok, extensions are ok, but I'd rather not rewrite all the Perl code in the world just to handle someone's idea of what Perl should have been if they were in charge ;-) I also would rather not be forced to use someone else's inextensible definition of OO that is too simple or too complex for my needs. Perl's got a good start on a very minimalist OO protocol. Let's make the minimal visible fixes we can to that, the maximal fixes to making it efficient, and the necessary fixes to make it more easily and compatabily extensible, so that multiple OO protocols can interoperate in the same program. Yours, &c, Tony Olekshy
Re: RFC 80 (v2) Exception objects and classes for builtins
Peter Scott wrote, in RFC 80 (v2): > > =item id > > Unique numeric identifier, assigned by perl developers. I'm loath to bother everyone with this, but to me the id of an object should be unique to each *instance* of the class. If we had my $e = Exception->New(id => "ABC.1234"); my $f = Exception->New(id => "ABC.1234"); then $e and $f would have the same id, but be different objects. In RFC 96 I've proposed called this attribute the "tag", in the sense of those little paper labels tied to merchandise with a string or the little embossed metal plate attached to a motor or to a pressure relief valve (that's what mechanical engineers call them). If we really want an id attribute, I think it should be "$self" or ++$foo for some base-module lexically scoped $foo. I can't think of any practial use for it right now, but that certainly doesn't mean there can't be any. > Line number exception was thrown at. > File exception was thrown in. Should this be line thrown at or line constructed at? Does anyone care? > =item data[(userdata)] > > User data, arbitrarily complex. If the user knew the underlying > object implementation, of course they could stick in attributes > with any names they wanted; but nothing should rely on that, so > this hook is provided. Something like this is probably a good idea, but as noted in RFC 96, "How to extend ivars and control namespace?". > Stringifying the object itself will yield the C attribute. Or perhaps a formatted combination of a subset of the attributes, as in RFC 96? > A C attribute was suggested to indicate what part of > perl is throwing the exception: IMO that is covered in the > exception class. Agreed. Yours, &c, Tony Olekshy
Re: RFC 95 (v1) Object Classes
Andy Wardley wrote: > > A key feature of this proposal is that object/class variable and > methods are indistinguishable to the user. The dot operator does > the right thing to call a method (if defined), or instead access a > variable, or follow a delegation reference, etc. i.e. > > $foo.bar > > is something like (assuming a blessed hash) > > UNIVERSAL::can($foo, 'bar') ? $foo->bar() : $foo->{'bar'}; > > '->' and '::' won't do that, and shouldn't. If we tried to overload > '::' or '->' to be more magical then we would break things all over > the place. I don't understand. Why don't you just have the class generator automatically create an accessor method for every declared instance variable and otherwise prevent access to the instance variable except via said method? Now methods and ivars look the same because ivars *are* methods (accessor methods), from the perspective of the class client. This also means that if you define visibility constraints for methods you more-or-less automatically get them for ivars, again because ivars are only visible via their accessor methods. That's how that Prothos::Class module I mentioned in a previous message (about RFC 92) works. Yours, &c, Tony Olekshy
Re: RFC 95 (v1) Object Classes
Andy ~ Since you didn't mention it in your references, you may want to check out RFC 92, Extensible Meta-Object Protocol -- Method Search at http://tmtowtdi.perl.org/rfc/92.pod RFC 92 considers an existing Perl 5 module we have that allows us to write code like the following, and it considers how to make Perl 6 better support this kind of extensibility via modules (rather than via new core functionality forced on us all). package MyClass; use Prothos::Class ISA => ParentClass; ivar Foo => Public,Write => Private; ivar Bar => Private, Default => "Hello, World"; ivar Baz => Protected, Default => {}; # Hash ivar. method HelloWorld => Public, sub { my ($I, %A) = @_; $I->Baz(Name => $A{Name}); # Ivar accessor. print $I->Baz("Name"), " says \"", $I->Bar, ".\n"; }; method Cogitate => Private, sub { ... }; method OverrideMe => Protected, Bind => Virtual, sub { ... }; Our little class generator does the things we need, without taxing our conceptual notion of what Perl OO looks like. As far as I can tell, RFC 95 doesn't do the things we need, while taxing our notion of what Perl OO looks like. So I guess you can chalk up my comment to be "skeptical", at least for now. Yours, &c, Tony Olekshy
Re: RFC 80 (v1): Exception objects and classes for builtins
Chaim Frenkel wrote: > > [ ... ] for me polymorphism is action-at-distance of the worst > stripe. Its the cheap and dirty way of doing OO. [...] I see > very little reason to have two methods with different signatures. Method signatures and polymorphism are orthogonal. The latter refers to different classes having differing implementations of a method of the same name (often via inheritance and overriding) and/or name + signature. Say you had a base class for exception objects that defines a method that returns whether or not Foo is true for that class. Say you derive a descendent class from that base class via inheritance, and then extended the derived class to add a new instance variable. Say you then, in your derived class, override the method that returns whether or not Foo is true to take your new instance variable into account. With polymorphism, one doesn't have to know whether one has an instance of the base class or an instance of the derived class (using kludgery such as isa), one can just say $o->IsFoo and both cases work as per Fooing. Signatures don't need to enter into it. For more information see http://www.cyberdyne-object-sys.com/oofaq2/body/typing.htm#S2.1 Yours, &c, Tony Olekshy
Re: RFC 85 (v1) All perl generated errors should have a
Chaim Frenkel wrote: > > [stuff about exception numbering] > > Hmm, I thought I saw another exception RFC pass by. > Yup, RFC 88, Tony Olekshy <[EMAIL PROTECTED]> > > Could you two folks get together and hash this out. RFC 88 goes to some trouble to seperate exception handling from exception objects. It doesn't care how exception objects are numbered, labelled, or titled; it only cares how they stringify and isa. However, RFC 88 does say: If Try's notions of the seperation of exception handling from the implementation of exception objects gains credence, there will need to be one or more RFCs on the matter of the built-in or core-module Exception class sanctioned by Perl 6. RFC 88 does provide a minimalist exception object base class, but it doesn't consider the manner in which this is extended, such as for numbering, labelling, or source-code tracking. It also doesn't consider the class hierarchy for exception objects. I will however be delighted to participate in the discussion of RFCs related to Perl 6's canonical exception class. We should probably move to [EMAIL PROTECTED] Yours, &c, Tony Olekshy
Re: RFC 80 (v1): Exception objects and classes for builtins
Peter Scott wrote: > > try { > } catch Exception::IO with { > } catch Exception::Socket with { > } otherwise { > }; Jonathan Scott Duff wrote: > > try { > } catch { > switch ($EXCEPTION->name) { > case IO { ... } > case Socket { ... } > } > } > > The catch clause would catch all exceptions. The one > thrown would be placed in a "global" $EXCEPTION variable. With the approach proposed in RFC 88 (Structured Exception Handling Mechanism), you could write that as: try { } catch { switch ($_[0]->name) { case IO { ... } case Socket { ... } } } There is no need for a "global". Let's take this discussion to [EMAIL PROTECTED], and give it a new subject. Yours, &c, Tony Olekshy [EMAIL PROTECTED]