Re: Clarification of S04 closure traits

2009-08-11 Thread Ben Morrow
Sorry for the delay in replying, but I was busy with other things and I
wanted to give other people a chance to reply. Since noone has, might it
be possible to get the attached patches committed? I'm not familiar with
the protocol for such things so, again, I'm sorry if I've got it wrong.

Ben

--- S04-control.pod.orig	2009-08-11 08:43:36.0 +0100
+++ S04-control.pod	2009-08-11 09:03:42.0 +0100
@@ -1232,6 +1232,21 @@
 before C, C, or C, since those are done at compile or
 process initialization time).
 
+If an exception is thrown through a block without a C block, the
+C, C and C blocks will be run at that point, with
+C<$!> set to the in-flight exception.  If there is no in-flight
+exception when these blocks are run, C<$!> will be C.  The last
+exception caught in the outer block is available as C<< OUTER::<$!> >>,
+as usual. 
+
+An exception thrown from an C block will abort the C
+queue, but one thrown from a C block will not.  The exceptions
+thrown by failing C and C blocks cannot be caught by a
+C in the same block, which implies that C blocks are not
+run if a C block fails.  If a C fails while an exception is in
+flight the C failure doesn't replace C<$!> but goes straight into
+C<$!.pending>.
+
 For blocks such as C and C that are run when exiting a
 scope normally, the return value (if any) from that scope is available
 as the current topic.  (It is presented as a C object.)
Index: S04-closure-traits/enter-leave.t
===
--- S04-closure-traits/enter-leave.t	(revision 27955)
+++ S04-closure-traits/enter-leave.t	(working copy)
@@ -2,7 +2,7 @@
 
 use Test;
 
-plan 11;
+plan 19;
 
 # L
 # L
@@ -92,4 +92,92 @@
 }), 1, 'leave triggers LEAVE {}';
 }
 
+{
+my $str;
+try {
+ENTER { $str ~= '(' }
+LEAVE { $str ~= ')' }
+$str ~= 'x';
+die 'foo';
+}
+is $str, '(x)', 'die calls LEAVE blocks';
+}
+
+{
+my $str;
+try {
+LEAVE { $str ~= $! // '' }
+die 'foo';
+}
+is $str, 'foo', '$! set in LEAVE if exception thrown';
+}
+
+{
+my $str;
+{
+LEAVE { $str ~= (defined $! ? 'yes' : 'no') }
+try { die 'foo' }
+$str ~= (defined $! ? 'aye' : 'nay');
+}
+is $str, 'ayeno', '$! not set in LEAVE if exception not thrown';
+}
+
+{
+my $str;
+try {
+$str ~= '(';
+try {
+ENTER { die 'foo' }
+$str ~= 'x';
+}
+$str ~= ')';
+}
+is $str, '()', 'die in ENTER caught by try';
+}
+
+{
+my $str;
+try {
+$str ~= '(';
+try {
+LEAVE { die 'foo' }
+$str ~= 'x';
+}
+$str ~= ')';
+}
+is $str, '(x)', 'die in LEAVE caught by try';
+}
+
+{
+my $str;
+try {
+$str ~= '(';
+try {
+ENTER { $str ~= '['; die 'foo' }
+LEAVE { $str ~= ']' }
+$str ~= 'x';
+}
+$str ~= ')';
+}
+is $str, '([])', 'die in ENTER calls LEAVE';
+}
+
+{
+my $str;
+try {
+ENTER { $str ~= '1'; die 'foo' }
+ENTER { $str ~= '2' }
+}
+is $str, '1', 'die aborts ENTER queue';
+}
+
+{
+my $str;
+try {
+LEAVE { $str ~= '1' }
+LEAVE { $str ~= '2'; die 'foo' }
+}
+is $str, '21', 'die doesn\'t abort LEAVE queue';
+}
+
 # vim: ft=perl6
Index: S04-closure-traits/pre-post.t
===
--- S04-closure-traits/pre-post.t	(revision 27955)
+++ S04-closure-traits/pre-post.t	(working copy)
@@ -9,7 +9,7 @@
 # TODO: 
 #  * Multiple inheritance + PRE/POST blocks
 
-plan 18;
+plan 25;
 
 sub foo(Num $i) {
 PRE {
@@ -125,4 +125,82 @@
 lives_ok { $pt.test(2) }, 'POST receives return value as $_ (succeess)';
 dies_ok  { $pt.test(1) }, 'POST receives return value as $_ (failure)';
 
+{
+my $str;
+{
+PRE  { $str ~= '('; 1 }
+POST { $str ~= ')'; 1 }
+$str ~= 'x';
+}
+is $str, '(x)', 'PRE and POST run on ordinary blocks';
+}
+
+{
+my $str;
+{
+POST  { $str ~= ')'; 1 }
+LEAVE { $str ~= ']' }
+ENTER { $str ~= '[' }
+PRE   { $str ~= '('; 1 }
+$str ~= 'x';
+}
+is $str, '([x])', 'PRE/POST run outside ENTER/LEAVE';
+}
+
+{
+my $str;
+try {
+{
+PRE { $str ~= '('; 0 }
+PRE { $str ~= '*'; 1 }
+ENTER   { $str ~= '[' }
+$str ~= 'x';
+LEAVE   { $str ~= ']' }
+POST{ $str ~= ')'; 1 }
+}
+}
+is $str, '(', 'failing PRE runs nothing else';
+}
+
+{
+my $str;
+try {
+{
+POST  { $str ~= 'x'; 0 }
+LEAVE { $str ~= 'y' }
+POST  { $str ~= 'z'; 1 }
+}
+}
+is $str, 'yx', 'failing POST runs LEAVE but not more POSTs';
+}
+
+{
+my $str;
+try {
+POST { $str ~= $! // ''; 1 }
+die 'foo';
+}
+is 

Re: Clarification of S04 closure traits

2009-07-28 Thread Ben Morrow

Moritz Lenz wrote:

Ben Morrow wrote:


- Presumably when an exception is thrown through a block, the LEAVE and
  POST queues are called (in that order).


POST was inspired from the Design By Contract department, and are meant
to execute assertions on the result. If you leave a block through an
exception you don't have a result, so I don't think running a POST block
makes sense.


OK, if POST is only for asserting on the return value that would make 
sense. But I thought POST was also supposed to be able to assert that 
global state had been left as it should, in which case it should be run 
even on exceptional exit? I'm not sure how any given POST block is 
supposed to distinguish between an ordinary undef return and an 
exception, though, to avoid checking the return value. Some sort of 
guarantee that $! is undef in a POST block unless an exception is 
currently being thrown might be helpful, but I'm not sure how that 
interacts with the Perl 6 exception model as a whole.



I'm not sure about running LEAVE blocks either, because that's what
CATCH blocks are for.


LEAVE blocks I am certain *should* be run, no matter how the block is 
exitted (well, unless a PRE fails: see below). They are equivalent to 
'finally' blocks in other languages, and AIUI they are for cleanup: 
closing files, tearing down database connections and the like.



What if a PRE block fails: is
  the POST queue on the same block called? (Do you have to satisfy your
  post-conditions even if your pre-conditions failed?)


I'd say that if a PRE block fails, nothing else is run (neither LEAVE
nor POST nor ...)


I agree that none of ENTER, LEAVE or the main body of the block should 
be run. However, if a POST block is asserting, say, that a global holds 
a valid value, shouldn't that still be checked even if your 
preconditions failed? I admit I haven't read much about DBC.



- If a POST block is called as a result of a thrown exception, and it
  fails, which exception 'wins'?


Question obsoleted by previous answer ;-)


...maybe :).


- Can a try block catch an exception thrown from its own ENTER or LEAVE
  queue? 


Yes.


OK. What about PRE/POST? It seems wrong somehow to be able to catch 
failure of your own pre-/post-conditions: they are meant for the caller 
to catch. Does that seem right?


Ben



Re: Clarification of S04 closure traits

2009-07-27 Thread Moritz Lenz
Ben Morrow wrote:
> Moritz Lenz wrote:
>> Ben Morrow wrote:
>>>
>>> - Presumably when an exception is thrown through a block, the LEAVE and
>>>   POST queues are called (in that order).
>> 
>> POST was inspired from the Design By Contract department, and are meant
>> to execute assertions on the result. If you leave a block through an
>> exception you don't have a result, so I don't think running a POST block
>> makes sense.
> 
> OK, if POST is only for asserting on the return value that would make 
> sense. But I thought POST was also supposed to be able to assert that 
> global state had been left as it should, in which case it should be run 
> even on exceptional exit? 

I never thought of that, because in the only other DBC-enabled language
that I've used (Eiffel) there are other assertions types for that (iirc
they are called "class invariants", but I'm not entirely sure) which are
run after every method call from the outside of the class, or something
along these lines.

I haven't seen such a thing in the spec in Perl 6, so either we invent
such a thing, or pick up your position (always run POST)

> I'm not sure how any given POST block is 
> supposed to distinguish between an ordinary undef return and an 
> exception, though, to avoid checking the return value. Some sort of 
> guarantee that $! is undef in a POST block unless an exception is 
> currently being thrown might be helpful, but I'm not sure how that 
> interacts with the Perl 6 exception model as a whole.

Same here.

>> I'm not sure about running LEAVE blocks either, because that's what
>> CATCH blocks are for.
> 
> LEAVE blocks I am certain *should* be run, no matter how the block is 
> exitted (well, unless a PRE fails: see below). They are equivalent to 
> 'finally' blocks in other languages, and AIUI they are for cleanup: 
> closing files, tearing down database connections and the like.

You're right here; it's the KEEP block that is only executed on normal exit.

>>> What if a PRE block fails: is
>>>   the POST queue on the same block called? (Do you have to satisfy your
>>>   post-conditions even if your pre-conditions failed?)
>> 
>> I'd say that if a PRE block fails, nothing else is run (neither LEAVE
>> nor POST nor ...)
> 
> I agree that none of ENTER, LEAVE or the main body of the block should 
> be run. However, if a POST block is asserting, say, that a global holds 
> a valid value, shouldn't that still be checked even if your 
> preconditions failed? I admit I haven't read much about DBC.

Same as for the first question.

>>> - If a POST block is called as a result of a thrown exception, and it
>>>   fails, which exception 'wins'?
>> 
>> Question obsoleted by previous answer ;-)
> 
> ...maybe :).

If not: $! is an aggregate of all exceptions thrown so far, so whichever
way we go, we don't lose any information. I fear that doesn't help you
very much, though, because it's different in Perl 5.

I personally prefer to know about the first exception, for example I
find it more informative that an open() fails than that a subsequent
print() to that file failed.

>>> - Can a try block catch an exception thrown from its own ENTER or LEAVE
>>>   queue? 
>> 
>> Yes.
> 
> OK. What about PRE/POST? It seems wrong somehow to be able to catch 
> failure of your own pre-/post-conditions: they are meant for the caller 
> to catch. Does that seem right?

Yes.
(Actually they are not really meant to be caught, because they always
reveal programming errors, but I guess sometimes you still can't avoid it).

I just fear that I haven't contributed much clarity at all :/

Cheers,
Moritz


Re: Clarification of S04 closure traits

2009-07-26 Thread Moritz Lenz
Ben Morrow wrote:
> I'm iworking on a patch for Perl 5 that implements the Perl 6 closure
> traits (ENTER/LEAVE/...) as special blocks. There are several details
> that aren't clear to me from either S04 or the spec tests; I apologize
> if these have been discussed before, as I haven't been following p6l.


No need to apologize; such things should be clear from reading the spec.

Note that my answers are in no way authoritative, just proposals on how
to handle it (and maybe starting point of useful discussions).

> I'm also not subscribed, so if people could keep me in the CC that would
> be appreciated.
> 
> - Presumably when an exception is thrown through a block, the LEAVE and
>   POST queues are called (in that order).

POST was inspired from the Design By Contract department, and are meant
to execute assertions on the result. If you leave a block through an
exception you don't have a result, so I don't think running a POST block
makes sense.
I'm not sure about running LEAVE blocks either, because that's what
CATCH blocks are for.

> What if a PRE block fails: is
>   the POST queue on the same block called? (Do you have to satisfy your
>   post-conditions even if your pre-conditions failed?)

I'd say that if a PRE block fails, nothing else is run (neither LEAVE
nor POST nor ...)

> - If a POST block is called as a result of a thrown exception, and it
>   fails, which exception 'wins'?

Question obsoleted by previous answer ;-)

> - Presumably if an ENTER block dies, the rest of that ENTER queue is
>   abandoned. Does that also apply to the LEAVE queue? What should
> 
> {
> LEAVE { say "leave1" }
> LEAVE { say "leave2"; die "foo"; }
> }
> 
>   print? Similarly POST: once one post-condition has failed, are
>   subsequent post-conditions checked?



> - Can a try block catch an exception thrown from its own ENTER or LEAVE
>   queue? 

Yes.

> For example in this case:
> 
> try {
> 
> try {
> ENTER { die "foo" }
> CATCH { default { say "caught inside" } }
> }
> 
> CATCH { default { say "caught outside" } }
> }
> 
>   which CATCH block gets the exception? What about PRE/POST: can you
>   CATCH failure of your own pre-/post-conditions?
> 
> - Does it make any difference in any of the above if 'die' is replaced
>   by 'exit'?

No idea, really.

HTH,
Moritz