TSa wrote:

> HaloO,
>
> Yuval Kogman wrote:
>
>> On Wed, Sep 28, 2005 at 11:46:37 -0500, Adam D. Lopresto wrote:
>>
>>> The recent thread on Expectuations brought back to mind something
>>> I've been
>>> thinking for a while.  In short, I propose that "use fatal" be on by
>>> default, and
>>> that "err" be turned into syntactic sugar for a very small try/CATCH
>>> block.
>>
>>

You already know that "err" is the low-precedence version of //, right?
What replaces that? I like "default" or "defaults" myself, but I'm never
really sure what the precedence actually IS. After all, "and"/"or" were
lower than assignment, so you could code:

$a = foo or die;

and get ($a or die). How does this work for the "err"/"defaults"
keyword? Does the low-precedence version move up, or is there an idiom I
don't understand?

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.

>>
>> I like it a lot. It gives the advantages of both the flexible, more
>> robust try/catch, and the (locally) concise, clear error return.
>
>
> I don't like it at all. I fear, that we mix two orthogonal concepts
> just because it is convenient.
>
> To me the statement
>
>   return 42;  # 1
>
> has to orthogonal meanings:
>
>   1) the current scope has reached its (happy) end
>   2) a specific result was determined
>
> We can vary on both of these dimensions *independently*!
> Which gives the remaining three cases:
>
>   return undef; #  0   unspecific result
>   fail   undef; # -1   no return with unspecific reason
>   fail   42;    # -2   no return but  determined reason
>
> In other words an exception means
>
>   return !caller;
>
> or in yet another way to describe my attitude: the least
> thing that *defines* an exception is that the dynamic
> scope in question has reached the conclusion that it
> is *not* going to give control back to its creator!


But it *does* give control, albeit briefly, back to its caller.

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.

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;

the try/catch paradigm essentially says:

I wanted to call <c>sub Dog foo()</c> but there may be times when I
discover, after making the call, that I really needed to call an anonymous
<c>sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }</c>.

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". 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.

Instead, I want PERL to fill that in for me: I believe that the default
error mechanism should debug my program, the shell script that calls my
program, and the actions (including blood alcohol content) of the user
of my program over the last 24 hours: lets leave <c>use autodebug;</c>
turned on by default.

The 'Exceptuation' proposal seems to me to include two things:

1. A 'RESUME' feature.
2. An implicit acknowledgement that the default implementations are
parallel:

{...
  CATCH  -> $e {throw $e;} # Going up?
  RESUME -> $r {resume $r;} # Going down?
}

The rest is optimization. If caller() includes an array of
continuations, then <c>throw</c> looks like a loop up the array:

  sub throw(Exception $e)
  {
    reverse caller() ==> { .continuation($! => $e) if does(CATCH); }
  }

But the default behavior (modulo threads) is going to unlink all the
stack frame pages when the continuation is invoked. 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 elaborate
skycastles. And it must be reentrant because of the possibility of
exceptions during the exception handling. Which means that the call
stack needs to be stored in the Exception. [The list of things in the
exception gets pretty long. I'm sure it's all a ref to the last page of
the call stack, so it doesn't gobble up much space, but there's a lot of
"and you'll want"s coming up.]

So <c>resume</c> is a multi, no? (Or it could just be a method:
$!.resume, but that doesn't read as well in a block that *really* should
be as readable as possible.) Also, any layer of exception handling may
do some nontrivial amount of work before encountering its own exception:

  {
    ...
    CATCH -> $e
    {
      do_some_work; # exception in here?
      do_more_work; # Or in here?
      resume $e;
    }
  }

Which kind of argues that either (A) very fine control of RESUME blocks
must be available:

{...
  CATCH -> $e
  {
    do_some_work;
    RESUME {...}
    do_more_work;
    RESUME {...}
  }
}

or that (B) very fine identification of exceptions be available,
*including from the p6 library*. Perhaps typed resumptions are as
important as typed exceptions?

sub copy_file($from, $to)
{
  ...
  RESUME -> $r
  {
    when Resumption::Microsoft::AbortRetryFail::Abort { last; }
    when Resumption::Microsoft::AbortRetryFail::Retry { continue; }
    when Resumption::Microsoft::AbortRetryFail::Fail    { return FAILED; }
  }
}

I don't actually like this, because I don't like the whole notion of
upper-case blocks: it's stuff that the really good perl IDE is going to
hide so that the "real" code shows through.

Also, of course, there's the question of leverage: throwing an exception
is a way of quickly saying "something happened outside the expected".
One enormous benefit of $perl[1..5] was that the exceptions were
huffmanized really highly: <c>die</c>. This was good not because
exceptions were common, but because they were uncommon, and allocating a
short token allowed the reader to bypass them quickly. The whole "or die
message" idiom worked for that reason:

if (print("Hello, world!") == -1)
{
  print STDERR "Could not write greeting message";
  exit 1;
}

vs.

print "Hello, world"
  or die "Could not write greeting message";

I think that moving the <c>die</c> into <c>print</c> is a win, but it
disconnects the diagnostic a little bit:

print "Hello, "
  or die "Could not write first part";
print "world!\n"
  or die "Could not write second part";

Becomes a single generic failure message thrown by:

print "Hello, ";
print "world!\n";

Abstractly, you want the generic-ness, but simultaneously want to think
that someone can catch these errors at the relevant place and quickly
tailor them. The right place, of course, is the sub or method that is
doing the work. I want to be able to CATCH local exceptions locally,
too! Not because "I don't know what to do", but in a more Knuth/literate
sense, I don't want to write about what to do in-line with my beautiful
algorithm:

sub greet
{
  print "Hello, " # or throw PrintException;
  print "world\n" # or throw PrintException;

  CATCH
  {
    when PrintException {...}
  }
}

But I want to know which PART of my elaborate greeting failed. In the p5
version I could discriminate between first-part failure and second-part
failures. Why has p6 backslid into mealy-mouthed genericity?

One way, Yuval's suggestion, is to make "err" be a quicky catch mechanism:

sub greet
{
  print "Hello, " err die "Could not write first part";
  print "world!\n" err die "Could not write second part";
}

But that's not abstracting anything. It just keeps the same, tedious,
check-every-result coding layout and gives it fractionally worse
Huffmanization. What I want is to catch my errors elsewhere, and still
be able to localize their solution. Cleverly, there's already a way to
do this: labels. All I need is to know where I'm at, label-wise, and
I've got a dandy way to mark my code up.

sub greet
{
FirstPart:
  print "Hello, ";

SecondPart:
  print "world!\n" ;

  CATCH
  {
    when PrintException
    {
      fprintf(STDERR, "Could not write %s part",
caller.label.tolower.s/part$//);
    }
  }
}

This is a little more important than it seems, because for resumptions
it will likely be important to know what work has been done. Likewise,
any and all kinds of error handlers will want to know about resources
that are allocated but need to be put back. The <c>finally</c> block is
the ultimate admission of failure in this regard:

[quote=http://java.sun.com/docs/books/tutorial/essential/exceptions/finally.html]
The final step in setting up an exception handler is to clean up before
allowing control to be passed to a different part of the program. You do
this by enclosing the clean up code within a |finally| block. The
|finally| block is optional and provides a mechanism to clean up
regardless of what happens within the |try| block. Use the |finally|
block to close files or to release other system resources.
[/quote]

But resumptions are going to let us FIX the problem that occurred when
we tried to open the 256th filehandle. So all the directory parsing,
file exploring, and whatnot is going to be *preserved*. Thus we don't
actually want to automatically finalize all that stuff unless we know
for sure that we aren't going to resume: even *later* binding. If there
were a FINALLY clause (let us pray for deliverance, here, banjos and
all) it couldn't be run until a CATCH block exited.

In the meantime, we're trying to resume from exception handling. So like
the gynecologist who changed jobs to become a motorcycle mechanic, we
want to do all our work by reaching through the tailpipe. The exception
[here we go again] needs to bundle along a stack that provides access to
the variable scope. Yet another use for a Pascal's <c>with</c> keyword:

# NB: Testing new MySql 5 server
BEGIN
{
  CATCH -> $e
  {
    when Exception::Database::Mysql::Connect
    {
      with $e.namespace
      {
        if $dsn ~~ 'mysql:'
        {
          $dsn.s/^mysql:/mysql5:/;
          $e.resume;
        }
      }
    }
  }
}

=Austin

Reply via email to