Could the RAII-styled second option be rewritten as follows? do |cond, handler| { cond.add(handler); do_some_stuff(); that_might_raise(); out_of_kittens(); }(OutOfKittens) |t| { UseAardvarksInstead }
This has the advantage of requiring neither block to be surrounded by parentheses, nor requiring any nebulous unused temporary variable. It also puts the error-handling code after the guarded code, if you're a fan of try/catch. (If this is just mind-bendingly bizarre or outright wrong, please excuse me as I've been up all night.) On Fri, Oct 19, 2012 at 7:48 PM, Graydon Hoare <gray...@mozilla.com> wrote: > Hi, > > If you're going to respond to this email, please read it in full, don't > just skim. > > I've been sketching a module in core::condition this week that > implements a condition-handling system on top of the TLS feature > (task::local_data) that Ben Blum added over the summer. > > This email is a very specific description of what I'm adding (many > people have asked) along with a very specific poll about how best to > structure the outermost part of the API. > > I'd appreciate replies here be kept focused on those points -- > clarifying questions + indication of preference on API -- as there are a > lot of new people on the list since we last discussed this, but we are > _not_ changing plan here. This is the same plan we've had for 6-ish > years, off-and-on, sketched numerous times, whenever it seems > implementable with the pieces lying around. I'm not interested in having > a big discussion of all the possible ways condition-systems can be > built, or (re-)exploring the design-space. > > Some references to the lurking plan here: > > https://mail.mozilla.org/pipermail/rust-dev/2012-May/001879.html > https://mail.mozilla.org/pipermail/rust-dev/2011-November/000999.html > https://github.com/mozilla/rust/issues/1945#issuecomment-4425610 > > Anyway, here is the system (again) in a nutshell: > > - Tasks have a task-local storage system (TLS). TLS maps keys to > values that stick to the task until released. The keys are > addresses, conventionally static addresses of items (this is a > hack for producing 'unique' keys, but it works). > > - Handlers are stack-closures that get pushed and popped from TLS. > They obey stack discipline and can capture their lexical environment > since they'll be popped before the pushing frame exits. > > - Handlers are put in TLS keyed to a condition. A condition is a > static, symbolic value used mostly for its TLS-keying and > method-dispatching capabilities. Conditions don't store values, > they just name situations that might occur and give-type-to any > values passed-to and returned-from potential handlers of such > situations. > > - Code can raise a condition whenever it likes. No static checking > of which conditions might occur where; no relationship to function > types. Raising of a condition in a module is indicated by the > condition being present in the module, by convention, or by > documentation. > > - Raising a condition can pass a value giving further details on > the condition, beyond just its type/name. If the raise is handled, > a value may return. The types of values raised-to and returned-from > handlers is specified by a condition. That is, a condition is > declared like "const OutOfKittens : Condition<T,U> = ..." where > T is the type of values passed to an OutOfKittens handler, and > U is the type of values returned from an OutOfKittens handler when > it is returning a solution to the condition. U might well be a > symbolic type such as an enum of reasonable recovery strategies. > > - There is no change to the fail/unwind system. Failure is still > unrecoverable, results in unwinding, and is the only possible > cause of unwinding. If a condition is handled, it doesn't unwind. > If it's unhandled, or if the handler fails, the task fails and > unwinds. This is not an unwind-rewind system or an unwind-catch > system. > > - Condition.raise is a normal function and does something very simple: > - look in TLS to see if there's a handler > - if so, call it and return the result to the raiser > - if not, fail > > - This means condition-handling happens _at site_ of raising. If > the handler returns a useful value, processing continues as if > nothing went wrong. It's _just_ a rendezvous mechanism for an > outer frame to dynamically provide a handler closure to an inner > frame where a condition occurs. > > Given that description, astute readers may note that there's not much to > this "system", mostly just convention and existing pieces. This is > intentional. It's supposed to be very minimal and only took about a day > to get working, beyond the miserable slog Ben already fought through > implementing TLS over the summer. > > Nonetheless I want to get it _right_ since this is probably going to be > a convention that sticks and gets used extensively across our libraries. > Getting it nailed down is a priority for the 0.5 cycle, as we're trying > to knock out the top few remaining toe-stubs when writing libraries, and > this is one of them. We've grown an unfortunate dependence on using the > core::Result type for managing recoverable-problems in library code and > I would like to move away from that in as many cases as practical, since > at-site handling tends to be much cleaner for users. Since 'Result' is > so clunky we wind up using unrecoverable 'fail' in a lot of cases too, > sometimes cases where having the option to recover makes sense. > > So: API survey. Modulo a few residual bugs on consts and function > regions (currently hacked around in the implementation), I have 3 > different APIs that all seem to work -- which I've given different names > for discussion sake -- and I'm trying to decide between them. They > mostly differ in the number of pieces and the location and shape of the > boilerplate a user of the condition system has to write. My current > preference is #3 but I'd like a show of hands on others' preferences. > > Here they are: > > 1. "Protect/handle", a.k.a. "make it look like try/catch": this version > pursues the arrangement with protected-code coming first and handlers > coming second. To do this, the protected-code has to be suspended in a > closure, which is then passed to a temporary struct, which is > responsible for both capturing the remaining handler-part and firing the > push/run/pop cycle together at the end. Uses of it look like this: > > let b = do OutOfKittens.protect { > do_some_stuff(); > that_might_raise(); > out_of_kittens(); > }; > > do b.handle |t| { > UseAardvarksInstead > } > > It's not really an ideal use of our 'do' syntax, nor are many other > variants of this API I've tried. It might read slightly better (or > worse) as such: > > (do OutOfKittens.protect { > do_some_stuff(); > that_might_raise(); > out_of_kittens(); > }).handle(|t| > UseAardvarksInstead > ); > > > 2. "Guard", a.k.a. "make it look like RAII": this version has only a > single "floating in space" handler-block that is assigned to a temporary > guard object in the current scope, and all subsequent code in that scope > is protected. Reads like this: > > { > let _g = OutOfKittens.guard(|t| UseAardvarksInstead); > do_some_stuff(); > that_might_raise(); > out_of_kittens(); > } > > Downsides of this mechanism are that a user might try to move _g out of > the current frame or otherwise tamper with it -- we might need to do > some tricks with the region system to prevent that -- and it gives rise > to this somewhat artificial _g variable and otherwise unclear extra > block to contain it. > > 3. "Trap/in", a.k.a. "the system I like the look of best": this is the > version Patrick suggested yesterday. It sets up the handlers in the head > of a do-block and then invokes the protected code. It's order-inverted > from protect/handle -- the handlers come before the protected code -- > but it still reads ok to my eyes, and seems to play nicely with our > existing syntax: > > do OutOfKittens.trap(|t| UseAardvarksInstead).in { > do_some_stuff(); > that_might_raise(); > out_of_kittens(); > } > > Opinions? Clarifying questions? > > Thanks, > > -Graydon > _______________________________________________ > Rust-dev mailing list > Rust-dev@mozilla.org > https://mail.mozilla.org/listinfo/rust-dev >
_______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev