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 data code 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 packages it as some kind of attachment to the return value. BTW, I think there are more data types that use this technique then one might think on first sight. Iteraters, junctions and lazy evaluation in general come to mind. Control structures are wrapped around their block tighter and more efficient then the above procedure. In particular the binding steps 3 and 5 are subject of optimizations.
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! -- $TSa.greeting := "HaloO"; # mind the echo!