Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-06 Thread Gábor Lehel
On Sat, May 18, 2013 at 2:24 PM, Gábor Lehel illiss...@gmail.com wrote:



 On Thu, May 16, 2013 at 7:58 PM, Graydon Hoare gray...@mozilla.comwrote:


 I'm sympathetic to the desire here, as with all attempts to get
 exceptions right. Sadly I've never really seen it; I don't think anyone
 has really worked out the right way to work with catchable-exceptions in
 a language.

 -Graydon


 What's problematic about exceptions in C++, and what forces you to worry
 about exceptions everywhere, is that code inside a try-block and code
 outside of it may share state, which the code in the try block may be in
 various stages of modifying (along with allocating resources) when the
 exception is thrown, potentially leaving an inconsistent state and/or
 leaked resources at the point where the exception is caught. Therefore all
 functions have to be very careful that any mutable state accessible to the
 function is in (or reverts to) a consistent state at any point where an
 exception might be thrown, and that resources aren't left dangling, because
 who knows whether that mutable state might not be on the outside of a
 try-block and the function on the inside of it.

 What if instead of that, the language enforced that code in try blocks
 could not share any state with code outside, only allowing for data to be
 transferred in at the beginning and out (whether a result or an exception)
 at the end, and ensured that all resources acquired inside the try block
 were destroyed after it exited, no matter how it did? That would free the
 authors of individual functions from having to care about any of it,
 because if an exception is passing through their code, that means that from
 their perspective the world is ending, everything they have access to
 will be destroyed, so they can do whatever they want and it won't matter a
 bit.

 If that sounds familiar at all, it's because I just described the
 semantics of Rust's existing try()*. I still suspect that the best of all
 worlds would be something with the semantics of try(), or close, and the
 implementation and performance of traditional EH. Is that unrealistic?


Am I way off base with this? An embarrassing misconception? To summarize my
train of thought

 * Catchable exceptions can be implemented
 * But we don't want to, because it would force everyone to think about
exception safety
 * That could however be avoided with appropriate restrictions
 * Rust's type system already gives us the tools to impose those
restrictions, as evidenced by them being imposed on `try()`
 * Therefore it should be possible to have much of the benefit of catchable
exceptions, without their drawbacks

-- 
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-06 Thread Daniel Micay
On Thu, Jun 6, 2013 at 11:59 AM, Gábor Lehel illiss...@gmail.com wrote:

 Am I way off base with this? An embarrassing misconception? To summarize my
 train of thought

  * Catchable exceptions can be implemented
  * But we don't want to, because it would force everyone to think about
 exception safety
  * That could however be avoided with appropriate restrictions
  * Rust's type system already gives us the tools to impose those
 restrictions, as evidenced by them being imposed on `try()`
  * Therefore it should be possible to have much of the benefit of catchable
 exceptions, without their drawbacks

The cost of spawning a task with try over a theoretical optimized
exception handling scheme will only be the small stack segment it
needs to allocate (likely cached). The scheduler will switch to it
immediately, and if it doesn't do I/O there won't be a switch back
until it's done.

I think someone will need to demonstrate a performance issue with the
completed scheduler before providing a special case in the language
will seem like a serious suggestion.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-06 Thread Graydon Hoare

On 06/06/2013 8:59 AM, Gábor Lehel wrote:


Am I way off base with this? An embarrassing misconception? To summarize
my train of thought

  * Catchable exceptions can be implemented
  * But we don't want to, because it would force everyone to think about
exception safety
  * That could however be avoided with appropriate restrictions
  * Rust's type system already gives us the tools to impose those
restrictions, as evidenced by them being imposed on `try()`
  * Therefore it should be possible to have much of the benefit of
catchable exceptions, without their drawbacks


No. The train of thought is that they _already are_ implemented to this 
level -- via task isolation[1] -- and people asking for catchable 
exceptions are (so far) actually asking for us to lift those 
restrictions[2], which we don't want to do.


-Graydon

[1] If it helps avoid wincing about the implied cost of spawning a 
task (allocating a segment, switching to it, and switching back on 
return) it might help to know that there are some serious discussions 
going on in the background about cactus stacks and the requisite 
scheduler hooks required to support cilk-like fork/join parallelism.


[2] And/or asking to add first class language support for the idioms 
in the form of new keywords, RTTI or additional control structures. In 
case this is not obvious: we are trying to move as much as possible 
these days _out_ of the core language and into libraries and macros. 
This usually results in faster iteration, less fragility, fewer 
personnel and development/integration bottlenecks, and overall better 
implementations.


___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-06-06 Thread Bill Myers


 Date: Thu, 16 May 2013 10:58:28 -0700
 From: gray...@mozilla.com
 To: bill_my...@outlook.com
 CC: rust-dev@mozilla.org
 Subject: Re: [rust-dev] Adding exception handling as syntax sugar with 
 declared exceptions
 
 On 12/05/2013 8:00 PM, Bill Myers wrote:
  This is a suggestion for adding an exception system to Rust that
  satisfies these requirements:
  1. Unwinding does NOT pass through code that is not either in a function
  that declares throwing exceptions or in a try block (instead, that
  triggers task failure)
  2. Callers can ignore the fact that a function throws an exception,
  resulting in task failure
  3. Callers can handle exceptions if desired, with the same syntax of
  C++, Java and C#
 
  (1) avoids the issue with C++ exception handling forcing to think about
  exception safety everywhere
  (2) avoids the issue with Java's checked exceptions forcing to
  explicitly handle or redeclare them
  (3) provides an easy-to-use exception mechanism
 
 Hi, sorry, I did mean to get back to this.
 
 I think this is ... unlikely to work out, or be something we incorporate 
 into our repo. For a few reasons:
 
- It's very late in the design cycle to be making changes of
  this scope. We're trying to stabilize.
 
- It'll be a big performance hit on code paths that want to use
  such exceptions. Things that look like just function calls
  turn into quite extensive operations. We're already struggling
  with the costs of a return-bool solution for unwinding on
  platforms that have difficult normal EH (#4342).
 
- Most seriously: it doesn't actually resolve (1) or (2), imo.
 
  (1) You'd still have to declare checked exceptions everywhere. But
  worse than in java, if you failed to declare them _anywhere_
  you wouldn't get a compilation error, but a runtime failure.
  This is like making C++ default to 'throw ()', which tends
  to surprise everyone.

Well, the idea is that exception would generally either be caught in the 
immediate caller or let go to task failure, with rare cases of propagation.

The target use case are things like str_to_int functions that would throw 
format exceptions that would be caught by the immediate caller in case they 
want to handle it.

  (2) You still have to write in worry about exceptions everywhere
  style inside a try-block or function-that-rethrows. Only
  get to avoid it when you're insulating yourself by the implicit
  throw () declaration.

Yes, but you can't always avoid this anyway, since in some cases operations are 
truly irreversible.

For instance, if you want to make three HTTP POST requests and the second 
fails, then the first will have gone through and cannot be reverted 
automatically in any way, so the logic to handle we did something and then 
failed, leaving us in a half-done way is fundamentally necessary in some case 
regardless of language design.

A limited version of this might be implementable by macros that could 
automatically generate a try_ version of a function with Result return type 
along with a version that fails.


The only other approach that can improve on this is to have support for 
reversible execution.

Basically, one would compile everything in two versions, where one of the 
versions keeps an undo log of memory writes. When a try block is entered, the 
compiler switches to the undo-log-enabled code, and when an exception is 
thrown, the log is played to undo everything. Hardware transactional memory 
like Haswell TSX can also be tried first if available.

Of course, this wouldn't be able to handle external function calls and 
modifications of shared memory via unsafe pointers: this could be handled by 
having a special unsafe construct that passes a closure to unwind the changes.

Calling irreversible functions like I/O would need to cause a transaction 
abort/throw an exception. Also, some things like RWARC modifications would be 
impossible to handle without a full STM implementation (with MVCC and so on).

The biggest drawback of this is that it will double code size and compilation 
time.

I'm not sure if it's worthwhile since it cannot work in general due to I/O, and 
simple cases can be handled with the syntax sugar proposed or doing the same by 
hand with Result return types and so on.


  ___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-05-18 Thread Gábor Lehel
On Thu, May 16, 2013 at 7:58 PM, Graydon Hoare gray...@mozilla.com wrote:


 I'm sympathetic to the desire here, as with all attempts to get
 exceptions right. Sadly I've never really seen it; I don't think anyone
 has really worked out the right way to work with catchable-exceptions in
 a language.

 -Graydon


What's problematic about exceptions in C++, and what forces you to worry
about exceptions everywhere, is that code inside a try-block and code
outside of it may share state, which the code in the try block may be in
various stages of modifying (along with allocating resources) when the
exception is thrown, potentially leaving an inconsistent state and/or
leaked resources at the point where the exception is caught. Therefore all
functions have to be very careful that any mutable state accessible to the
function is in (or reverts to) a consistent state at any point where an
exception might be thrown, and that resources aren't left dangling, because
who knows whether that mutable state might not be on the outside of a
try-block and the function on the inside of it.

What if instead of that, the language enforced that code in try blocks
could not share any state with code outside, only allowing for data to be
transferred in at the beginning and out (whether a result or an exception)
at the end, and ensured that all resources acquired inside the try block
were destroyed after it exited, no matter how it did? That would free the
authors of individual functions from having to care about any of it,
because if an exception is passing through their code, that means that from
their perspective the world is ending, everything they have access to
will be destroyed, so they can do whatever they want and it won't matter a
bit.

If that sounds familiar at all, it's because I just described the semantics
of Rust's existing try()*. I still suspect that the best of all worlds
would be something with the semantics of try(), or close, and the
implementation and performance of traditional EH. Is that unrealistic?



* Along with the ability to actually observe the thrown exception, but IINM
that's planned. And I guess you could have multiple variations on how to
handle catching-and-or-rethrowing. If the plan is to use dynamic typing for
the exception object you could have something like:

implT ResultT, ~Typeable {
fn catchE: Typeable(self, hndlr: fn(ex: ~E) - T) - T {
match self {
Ok(res) = res,
Err(ex) = {
match ex.cast::E() {
Ok(casted_ex) = hndlr(casted_ex),
Err(uncasted_ex) = fail!(uncasted_ex)
}
}
}
}
}

That doesn't really do catch block chaining well and I probably made 10
type errors, but oh well.


--
Your ship was destroyed in a monadic eruption.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-05-16 Thread Graydon Hoare

On 12/05/2013 8:00 PM, Bill Myers wrote:

This is a suggestion for adding an exception system to Rust that
satisfies these requirements:
1. Unwinding does NOT pass through code that is not either in a function
that declares throwing exceptions or in a try block (instead, that
triggers task failure)
2. Callers can ignore the fact that a function throws an exception,
resulting in task failure
3. Callers can handle exceptions if desired, with the same syntax of
C++, Java and C#

(1) avoids the issue with C++ exception handling forcing to think about
exception safety everywhere
(2) avoids the issue with Java's checked exceptions forcing to
explicitly handle or redeclare them
(3) provides an easy-to-use exception mechanism


Hi, sorry, I did mean to get back to this.

I think this is ... unlikely to work out, or be something we incorporate 
into our repo. For a few reasons:


  - It's very late in the design cycle to be making changes of
this scope. We're trying to stabilize.

  - It'll be a big performance hit on code paths that want to use
such exceptions. Things that look like just function calls
turn into quite extensive operations. We're already struggling
with the costs of a return-bool solution for unwinding on
platforms that have difficult normal EH (#4342).

  - Most seriously: it doesn't actually resolve (1) or (2), imo.

(1) You'd still have to declare checked exceptions everywhere. But
worse than in java, if you failed to declare them _anywhere_
you wouldn't get a compilation error, but a runtime failure.
This is like making C++ default to 'throw ()', which tends
to surprise everyone.

(2) You still have to write in worry about exceptions everywhere
style inside a try-block or function-that-rethrows. Only
get to avoid it when you're insulating yourself by the implicit
throw () declaration.

I'm sympathetic to the desire here, as with all attempts to get 
exceptions right. Sadly I've never really seen it; I don't think anyone 
has really worked out the right way to work with catchable-exceptions 
in a language.


-Graydon

___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


[rust-dev] Adding exception handling as syntax sugar with declared exceptions

2013-05-12 Thread Bill Myers
This is a suggestion for adding an exception system to Rust that satisfies 
these requirements:
1. Unwinding does NOT pass through code that is not either in a function that 
declares throwing exceptions or in a try block (instead, that triggers task 
failure)
2. Callers can ignore the fact that a function throws an exception, resulting 
in task failure
3. Callers can handle exceptions if desired, with the same syntax of C++, Java 
and C#

(1) avoids the issue with C++ exception handling forcing to think about 
exception safety everywhere
(2) avoids the issue with Java's checked exceptions forcing to explicitly 
handle or redeclare them
(3) provides an easy-to-use exception mechanism

The specification is based on adding some syntax sugar, which is then 
transformed by the compiler into the current Rust syntax, and thus does not 
really alter runtime behavior (but it is also possible to implement this with 
traditional unwinding if desired).

Functions can be declared to throw exceptions, and if the conceptual unwinding 
process would have to pass through a function call that does not declare 
throwing an exception of that type, task failure is invoked instead.

To integrate with the condition system, one can raise a condition instead of 
throwing, and declare the condition type and handlers with throws, allowing 
them to throw exceptions if desired (note that of course this will just result 
in task failure unless the function raising the condition is declared to throw).

It is of course possible to code using this style by hand, but the syntax 
sugar allows to do it in a much terser way, and allows to convert functions 
that use task failure to functions that throw exceptions without changing the 
source code of callers that don't want to handle them.

* New syntax added:

- throws E1, E2, ... modifier on functions and closures
- try/catch block
- try() expression
- throw e statement

* New type declarations added:
enum Result1T, E1
{
  Ok(T),
  Fail(E1)
}

enum Result2T, E1, E2
{
  Ok(T),
  Fail1(E1),
  Fail2(E2)
}

... and so on...

* Transformations to implement the system:

Everywhere:
fn foo() - T throws E1 [similar with closures] = fn foo() - Result1T, E1
fn foo() - T throws E1, E2 [similar with closures] = fn foo() - Result2T, 
E1, E2
try(f(args)) [if f declared with throws] = f(args)
f(args)   [if f returns T and is declared with throws, not inside try()] = 
match f(args) {Ok(x) = x, Fail1(e) = throw e, Fail2(e) = throw e, ...}

In functions declared throws:
return v = return Ok(v)

try {try_body} catch(e1: E1) {catch_body} catch(e2: E2) {catch_body}
=
let mut e1_: OptionE1 = None;
let mut e2_: OptionE2 = None;
'try_: loop {try_body; break;}
if e1_.is_some() { let e1 = e1_.unwrap(); catch1_body }
else if e2_.is_some() { let e2 = e2_.unwrap(); catch2_body }

throw e, if there is any containing try block that has a catch block with 
argument eK with type EK such that e is convertible to EK (taking the innermost 
suitable try block and the first suitable catch block)
= eK_ = Some(e); break 'try_

throw e, if there are no suitable try blocks but the function is declared 
throws and e is convertible to throws type EK
= return FailK(e)

throw e, otherwise, if e implements ToStr:
= fail!(e.to_str())

throw e, otherwise:
= fail!()

  ___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev