Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
On Sat, Oct 01, 2005 at 05:57:54 -0400, Austin Hastings wrote: > Internally, it may be the same. But with exceptions, it's implemented by > someone other than the victim, and leveraged by all. That's the kind of > abstraction I'm looking for. My problem with the whole notion of "Either > errorMessage resultingValue" in Haskell is that we _could_ implement it > in perl as "Exception|Somevalue" in millions of p6 function signatures. > But I don't _want_ to. I want to say "MyClass" and have the IO subsystem > throw the exception right over my head to the top-level caller. In haskell it's the job of the Either monad to let you pretend you aren't doing Exception|Somevalue everywhere. You can sequence operations in a deeply nested manner, and then 'fail' at some point. Then control flow will just pop back up all the way with the error, instead of trying to continue. You don't really need to say 'Either ... ...', you just use do notation. > For appropriate definitions of both 'elegant' and 'convenient'. Java > calls this 'checked exceptions', and promises to remind you when you > forgot to type "throws Exception::Math::DivisionByZero" in one of a > hundred places. I call it using a word to mean its own opposite: having > been exposed to roles and aspects, having to code for the same things in > many different places no longer strikes me as elegant or convenient. I agree with that wholeheartedly, but in haskell you are making no obligation towards the shape of an exception - it can be 'Either thing Error' where Error is any data type you like. In this sense haskell is just as flexible but requires more abstraction than perl etc. It has it's merits - it's safer, and more reusable. It tends to win ICFP contests, and so forth. However, to just get that thing working real fast, without having to pay too much when the context becomes maintenance instead of development, i think Perl 6 will be the most suitable language in the world. > Right. At some level, you're going to have to do that. This to me is > where the "err" suggestion fits the most comfortably: "err" (or "doh!" > :) is a keyword aimed at ad-hoc fixes to problems. It smooths away the > horrid boilerplate needed for using exceptions on a specific basis. Which is why it's such a nice propsal =) > As syntactic sugar goes, it's not powerful enough yet. err next # catch all err Pattern, next # catch some Putting my code where my mouth is: sub infix: (&lhs is delayed, Pattern ?$match = Any, &rhs is delayed) { lhs; CATCH { when $match { rhs } default { die } } } Ofcourse, these can also stack: my $fh = open "file err rx/Permission Denied/, next err rx/No such file/, die; But i don't think this is very useful for one or at the very most two catches - for anything else it's ad-hoc nature just doesn't scale as nicely as CATCH blocks, which can be applied to several error generating blocks of code at once. Ofcourse, true acid heads can always say: do { ...; ...; } err ..., ... err ..., ...; but that's their problem. =) > The last sentence is telling, I think. The run-time system SHOULD take > as much care as possible. And rub my feet. Yes =) > True for any method that invokes exit(), no? Or that says NEXT on a > label outside its scope. Well, that's a semantic detail. The issue is that those methods *can* return, but don't. A continuation will never return - because it already has another place to return - the place that created it. This is ignoring CPS, ofcourse, in which every return and every call is a continuation. While this may be true under the hood, this is not what the average Perl 6 user can observe. > The scenario is that I try something (error_throwing_code) and catch an > exception. Then while showing a dialog box to the user, for example, I > get another exception: not enough handles or whatever. So someone higher > than me resolves that on my behalf, then resumes me. I'm still trying to > resume the error thrown earlier: Yes, that should work. > Now I need to ask, what happens when show_dialog_box throws an > exception? Presumably, I don't catch it in this code path, or there will > be a stack fault shortly. If the exception from show_dialog_box was thrown, and another CATCH handler fixed it for you, you don't need to worry about it - you can never know because you don't get access to that exception. It's as if it was never propagated. > One possibility is that the catcher of an exception knows little or > nothing about the innards of the thrower. It's the job of exception classes to bridge these - they have a class, and any number of attributes. Exception::IO::Open::PermissionDenied In fact I suspect that Exception::IO::Open enforces a certain type of fix, too: class Exception::IO
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
TSa wrote: > > The view I believe Yuval is harboring is the one examplified > in movies like The Matrix or The 13th Floor and that underlies > the holodeck of the Enterprise: you can leave the intrinsic > causality of the running program and inspect it. Usually that > is called debugging. But this implies the programmer catches > a breakpoint exception or some such ;) > > Exception handling is the programmatic automatisation of this > process. As such it works the better the closer it is in time > and context to the cause and the more information is preserved. > But we all know that a usefull program is lossy in that respect. > It re-uses finite resources during its execution. In an extreme > setting one could run a program *backwards* if all relevant > events were recorded! > The current state of the art dictates that exceptions are to be avoided when it is possible to handle the error in-line. That "exceptions should only be used for exceptional cases, and anything you encounter in the manual pages is not exceptional." I don't agree with this, because it is IMO effectively saying "We had this powerful notion, but it turned out to be difficult to integrate post-hoc into our stack-based languages, so we're going to avoid it. Rather than admitting defeat, though, we're going to categorize it as some kind of marginal entity." I don't see exceptions as necessarily being outside the intrinsic causality of the running program. They are non-traditional forms of flow control: event-based programming, if you will, in an otherwise sequential program. We do much the same thing when we talk about coroutines: violate the traditional stack model. We do the same thing again when we talk about aspects: de-localize processing of certain (ahem) aspects of the problem domain. The telling part of aspects, though, was the the first popular implementation (AspectJ) required a preprocessor and a special markup language to implement. Why? Because nobody uses extensibility and Java in the same sentence. I guess aspects are traditional in that regard, though: remember CFront. Perl, OTGH, doesn't have the poor body-image or whatever it is that keeps people afraid to change the syntax. >> It can't be a method because it never returns to it's caller - it's > > > It beeing the CATCH block? Ahh, no. "It" in this case is the ".resume" call. My question was is resume a multi, an object method, or what? >> This is because the types of exceptions I would want to resume are >> ones that have a distinct cause that can be mined from the exception >> object, and which my code can unambiguously fix without breaking the >> encapsulation of the code that raised the exception. > > > Agreed. I tried to express the same above with my words. > The only thing that is a bit underspecced right now is > what exactly is lost in the process and what is not. > My guiding theme again is the type system where you leave > information about the things you need to be preserved to > handle unusual cicumstances gracefully---note: *not* > successfully, which would contradict the concept of exceptions! This is the classical view of exceptions, and so it is subject to the classical constraints: you can't break encapsulation, so you can't really know what's going when the exception occurs. The reason I like the "with" approach is that it lets us delocalize the processing, but does _not_ keep the "exceptions are violent, incomprehensible events which wrench us from our placid idyll" mentality. In that regard, exceptuations are "resumable gotos". =Austin
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
Yuval Kogman wrote: >On Thu, Sep 29, 2005 at 13:52:54 -0400, Austin Hastings wrote: > > [Bunches of stuff elided.] >>A million years ago, $Larry pointed out that when we were able to use >>'is just a' classifications on P6 concepts, it indicated that we were >>making good forward progress. In that vein, let me propose that: >> >>* Exception handling, and the whole try/catch thing, IS JUST An awkward >>implementation of (late! binding) run-time return-type MMD. >> >> > >Exception handling is just continuation passing style with sugar. > >Have a look at haskell's either monad. It has two familiar keywords >- return and fail. > > >Every statement in a monadic action in haskell is sequenced by using >the monadic bind operator. > >The implementation of >>=, the monadic bind operator, on the Either >type is one that first check to see if the left statement has >failed. If it does, it returns it. If it doesn't it returns the >evaluation of the right hand statement. > >Essentially this is the same thing, just formalized into a type > Internally, it may be the same. But with exceptions, it's implemented by someone other than the victim, and leveraged by all. That's the kind of abstraction I'm looking for. My problem with the whole notion of "Either errorMessage resultingValue" in Haskell is that we _could_ implement it in perl as "Exception|Somevalue" in millions of p6 function signatures. But I don't _want_ to. I want to say "MyClass" and have the IO subsystem throw the exception right over my head to the top-level caller. I guess that to me, exceptions are like aspects in they should be handled orthogonally. Haskell's "Either" doesn't do that -- it encodes a union return type, and forces the call chain to morph whenever alternatives are added. The logical conclusion to that is that all subs return "Either Exception or Value", so all types should be implicitly "Either Exception or {your text here}". If that's so, then it's a language feature and we're right back at the top of this thread. >>Specifically, if I promise you: >> >> sub foo() will return Dog; >> >>and later on I actually wind up giving you: >> >> sub foo() will return Exception::Math::DivisionByZero; >> >> > >In haskell: > > foo :: Either Dog Exception::Math::DivisionByZero > >e.g., it can return either the expected type, or the parameter. > >Haskell is elegant in that it compromises nothing for soundness, to >respect referential integrity and purity, but it still makes thing >convenient for the programmer using things such as monads > > > For appropriate definitions of both 'elegant' and 'convenient'. Java calls this 'checked exceptions', and promises to remind you when you forgot to type "throws Exception::Math::DivisionByZero" in one of a hundred places. I call it using a word to mean its own opposite: having been exposed to roles and aspects, having to code for the same things in many different places no longer strikes me as elegant or convenient. >>the try/catch paradigm essentially says: >> >>I wanted to call sub Dog foo() but there may be times when I >>discover, after making the call, that I really needed to call an anonymous >>sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }. >> >> > >Yes and no. > >The try/catch mechanism is not like the haskell way, since it is >purposefully ad-hoc. It serves to fix a case by case basis of out >of bounds values. Haskell forbids out of bound values, but in most >programming languages we have them to make things simpler for the >maintenance programmer. > > Right. At some level, you're going to have to do that. This to me is where the "err" suggestion fits the most comfortably: "err" (or "doh!" :) is a keyword aimed at ad-hoc fixes to problems. It smooths away the horrid boilerplate needed for using exceptions on a specific basis. do_something() err fix_problem(); is much easier to read than the current { do_something(); CATCH { fix_problem(); }} by a lot. But only in two conditions: first that all exceptions are identical, and second that the correct response is to suppress the exception. To me that fails because it's like "Candy Corn": you only buy it at Halloween, and then only to give to other people's kids. As syntactic sugar goes, it's not powerful enough yet. >>We're conditionally editing the return stack. This fits right in with >>the earlier thread about conditionally removing code from the inside of >>loops, IMO. Once you open this can, you might as well eat more than one >>worm. Another conceptually similar notion is that of AUTOLOAD. As a perl >>coder, I don't EVER want to write >> >> say "Hello, world" >>or die "Write to stdout failed."; >> >>-- it's "correct". It's "safe coding". And it's stupid for a >>whole bunch of reasons, mostly involving the word "yucky". >> >> > >It's incorrect because it's distracting and tedious. > >http://c2.com/cgi/wiki?IntentionNotAlgorithm > >Code which does it is, IMHO bad code because
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
On Fri, Sep 30, 2005 at 17:09:23 +0200, TSa wrote: > And it is the type system that guaranties the availability > of the required information e.g. in $!. $! is polymorphic. Since CATCH is a topcializer, and you use when blocks to case over $!, you can check to see if it has the metadata you want yourself: CATCH { when It::Is::An::Error::I::Can::Deal::With { deal_with_error_of_kind_foo($!); # it may resume } } > >Reentrancy is an implementation detail best left unmentioned. > > the reentrance proplem is just there No, it's not - the solution is that $! is bound lexically to the CATCH block. Exceptions that redefine $! keep the old value in the exception stack inside of each exception, but this does not trample any CATCH block. It'd be outright stupid to make CATCH { handle_error(); warn "$! was thrown within the code yadda"; } print out completely bogus info becaue handle_error overwrote $!. We can even deal with this in perl 5: eval { bad_code }; if ($@) { { local $@; code_that_might_die_in_a_handler; } warn "$! was thrown within bad_code"; } > Just to synchronize our understanding, I see the following > equivalences from the data and code domains > > datacode > > class = sub > instance = invocation What? > To illustrate my view consider didn't help. > The view I believe Yuval is harboring is the one examplified > in movies like The Matrix or The 13th Floor and that underlies > the holodeck of the Enterprise: you can leave the intrinsic > causality of the running program and inspect it. Usually that > is called debugging. But this implies the programmer catches > a breakpoint exception or some such ;) Err, no. What I want is for perl 6 to help me write high quality user interfaces easily. One aspect of making this easy is letting me bridge between exception throwing code and the UI layer using continuations. > Exception handling is the programmatic automatisation of this > process. As such it works the better the closer it is in time > and context to the cause and the more information is preserved. I read that as: "As such it works the better the farther it is in time and context from the UI code" and I must disagree, but it is a matter of style. As for information preserval: * every object that inherits Exception can have an arbitrary number of attributes. * the base class Exception has code to make it be thrown, or rethrown cleanly by making the previous value of $! available as an attribute of the new value of $! * Exception's constructor knows exactly where everything happenned (for error reporting, *AND* resuming) * a transition into a 'use fatal' lexical scope will cause the throw method to be called on a value being returned, if it is an exception. * if the next statement to be executed is not inside a CATCH block, the error object deletes it's .resume continuation, to clean up the lexical scope that is still alive inside it. This should be optional. > But we all know that a usefull program is lossy in that respect. > It re-uses finite resources during its execution. In an extreme > setting one could run a program *backwards* if all relevant > events were recorded! That's a job for omniscient debuggers. Languages with continuations are not debuggers. Exceptions are not raised for every opcode, just to record the flow. Exceptions normally do not happen. > >Yes, even signals and exceptions. > >The runtime is responsible for making these as fast as possible > >without being unsafe. > > Hmm, I would see the type system in that role. It has all the > information of the interested parties in a longjump. If it knows > there are no potential handlers The type system has nothing to do with code reentrency due to an icky implementation that shares code. Since malloc cannot be safely called within a C level signal handler, the runtime needs to be responsible for setting the virtual machine instruction pointer to the signal handling code, and to mark a flag, and at the next opcode dispatch (no longer an unsafe place) the signal handler will really happen. This has nothing at all to do with the type system, and doesn't even have anything to do with perl - this is parrot (or whoever)'s job. > >It can't be a method because it never returns to it's caller - it's > > It beeing the CATCH block? No, .resume. I take it back, it could be a method that invokes a continuation. > metric for dispatch applies. BTW, how is the signature of a CATCH > block given? Simply CATCH is just a topicalizer and a trait on the lexical scope. Within it $! is topicalized, and you use when: CATCH {
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
HaloO, Yuval Kogman wrote: The try/catch mechanism is not like the haskell way, since it is purposefully ad-hoc. It serves to fix a case by case basis of out of bounds values. Haskell forbids out of bound values, but in most programming languages we have them to make things simpler for the maintenance programmer. My view here is that the parameters in the code pick a point in the range from free on the one end and bound at the other end. The unifying concept is constraints. So I see the following boundary equalities: unconstraint = free # 0.0 contstraint # 0.0^..^1.0 fully constraint as the black and white ends with shades of gray in the middle. And it is the type system that guaranties the availability of the required information e.g. in $!. In that sense a sub with a CATCH block is a different type than one without. This difference is taking into account when dispatching exceptions. Reentrancy is an implementation detail best left unmentioned. Uh ohh, in an imperative language with shared state outside the unshared state of multiple invocations of one sub the reentrance proplem is just there. Interestingly it is easily unifyable with shared data. Assume that every bit of code you can run in perl 6 is first class code - it gets safety, calls, control flow, exceptions, and so forth. Just to synchronize our understanding, I see the following equivalences from the data and code domains datacode class = sub instance = invocation To illustrate my view consider class foo { has ... method ... } and match with sub foo { has ... # my named parameters defined in body proposal BEGIN ... CATCH ... label: } What I want to say is that calling a sub means creating an instance of the class that describes---or *constrains*--- the potential invocations. If such an invocation is left lying in memory unfinished we have a continuation. How concurrent these continuations are stepped in real time with respect to their respective inner causal logic is irrelevant to the concept. But *causality* is important! The view I believe Yuval is harboring is the one examplified in movies like The Matrix or The 13th Floor and that underlies the holodeck of the Enterprise: you can leave the intrinsic causality of the running program and inspect it. Usually that is called debugging. But this implies the programmer catches a breakpoint exception or some such ;) Exception handling is the programmatic automatisation of this process. As such it works the better the closer it is in time and context to the cause and the more information is preserved. But we all know that a usefull program is lossy in that respect. It re-uses finite resources during its execution. In an extreme setting one could run a program *backwards* if all relevant events were recorded! Yes, even signals and exceptions. The runtime is responsible for making these as fast as possible without being unsafe. Hmm, I would see the type system in that role. It has all the information of the interested parties in a longjump. If it knows there are no potential handlers It can't be a method because it never returns to it's caller - it's It beeing the CATCH block? Then I think it *is* in a certain way a method with $! as it's invocant. HiHi and here a locality metric for dispatch applies. BTW, how is the signature of a CATCH block given? Simply CATCH SomeException {...} or is inspection with cascaded when mandatory? a continuation because it picks up where the exception was thrown, I would say it is given information about. In a way an exception handler is dispatched on the type of exception. and returns not to the code which continued it, but to the code that would have been returned to if there was no exception. This is the thing that I see is hardly possible from a far away scope. But fair enough for closely related code. It is, IMHO a multi though. There is no reason that every continuation cannot be a multi, because a continuation is just a sub. I don't know if there are method continuations - i guess there could be, but there's no need to permutate all the options when the options can compose just as well. My view is that a (free) method type becomes a continuation as follows: 1) the invocant type is determined 2) the dispatcher selects a matching target 3) this method maker object (like a class for constructing data objects) is asked to create a not yet called invocation and bind it to the invocant at hand 4) at that moment we have a not yet invoked sub instance, so plain subs just start here 5) The signature is checked and bound in the environment of the calling scope, the callers return continuation is one of the args 6) then this invocation is activated 7a) a return uses the return continuation in such a way that the invocation is abandoned after the jump b) a yield keeps the continuation just like a constructed object and p
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
On Thu, Sep 29, 2005 at 13:52:54 -0400, Austin Hastings wrote: > You already know that "err" is the low-precedence version of //, right? > What replaces that? I like "default" or "defaults" myself, Yes, he proposed 'dor'. As I see it err is like this: sub infix: ($lhs is delayed, $rhs is delayed) { # we really need thunk parameters return $lhs dor $rhs; CATCH { return $rhs } } So really 'err' is just 'dor' with an added error silencer. > On the primary hand, I don't like the idea of using "err" as a try/catch > because that's putting exception handling in line with the primary code. > See FMTYEWT below. Isn't no fatal; my $fh = open $file or die; ... my $fh = open $file or next; also putting exception handling in line with the primary code? Isn't that useful? Exception handling should be put in wherever there is well defined behavior on how error conditions should be dealt with. It's Perl's job to make this as easy as possible for the programmer. If we have a module called File::Slurp::Some, which doesn't really care if some of the files don't exist, it's code would, IMHO be much more readable with open $file err next; than a catch block. The reason for this is that a catch block is always detached from the code. However, if the error is part of the normal flow (it is no longer exceptional for the file to not exist), what benefit does the added detachment give? > A million years ago, $Larry pointed out that when we were able to use > 'is just a' classifications on P6 concepts, it indicated that we were > making good forward progress. In that vein, let me propose that: > > * Exception handling, and the whole try/catch thing, IS JUST An awkward > implementation of (late! binding) run-time return-type MMD. Exception handling is just continuation passing style with sugar. Have a look at haskell's either monad. It has two familiar keywords - return and fail. Every statement in a monadic action in haskell is sequenced by using the monadic bind operator. The implementation of >>=, the monadic bind operator, on the Either type is one that first check to see if the left statement has failed. If it does, it returns it. If it doesn't it returns the evaluation of the right hand statement. Essentially this is the same thing, just formalized into a type > Specifically, if I promise you: > > sub foo() will return Dog; > > and later on I actually wind up giving you: > > sub foo() will return Exception::Math::DivisionByZero; In haskell: foo :: Either Dog Exception::Math::DivisionByZero e.g., it can return either the expected type, or the parameter. Haskell is elegant in that it compromises nothing for soundness, to respect referential integrity and purity, but it still makes thing convenient for the programmer using things such as monads > the try/catch paradigm essentially says: > > I wanted to call sub Dog foo() but there may be times when I > discover, after making the call, that I really needed to call an anonymous > sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }. Yes and no. The try/catch mechanism is not like the haskell way, since it is purposefully ad-hoc. It serves to fix a case by case basis of out of bounds values. Haskell forbids out of bound values, but in most programming languages we have them to make things simpler for the maintenance programmer. > We're conditionally editing the return stack. This fits right in with > the earlier thread about conditionally removing code from the inside of > loops, IMO. Once you open this can, you might as well eat more than one > worm. Another conceptually similar notion is that of AUTOLOAD. As a perl > coder, I don't EVER want to write > > say "Hello, world" > or die "Write to stdout failed."; > > -- it's "correct". It's "safe coding". And it's stupid for a > whole bunch of reasons, mostly involving the word "yucky". It's incorrect because it's distracting and tedious. http://c2.com/cgi/wiki?IntentionNotAlgorithm Code which does it is, IMHO bad code because obviously the author does not know where to draw the line and say this is good enough, anything more would only make it worse. > But I acknowledge that through the miracle of broken pipes, it can > legitimately happen that stdout will fail while stderr is a viable > diagnostic mechanism. the default $SIG{PIPE} handler is a wonderful example of how nice exception handling is for naive code. $SIG{PIPE}'s only problem is that it's completely non standard. > But the default behavior (modulo threads) is going to unlink all the > stack frame pages when the continuation is invoked. Forget the stack... Perl is CPS under the hood, even if it's optimized not to be that way. > So there has to be yet another copy of the links to the stack, > because the exception handling will want to call functions and > build who-knows-what elaborat