Am Do., 9. Juli 2020 um 06:32 Uhr schrieb John Cowan <xxxxxx@ccil.org>: > On Wed, Jul 8, 2020 at 10:28 AM Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote: > >> We have also solved the issue of trapping unpredictable exceptions by >> adding a mandatory 'pred?' argument to 'either-guard'.' > > I had actually forgotten that, which somewhat reconciles me to it. But I still want it to be a (first-class) procedure, not a (second-class) macro, something like this: (exception->either exeception-pred thunk). This is a little more verbose (an extra lambda) in the case you give, but brings more flexibility in use: it can, for example, be mapped over a list of thunks to be invoked to produce a list of Eithers. Here's an untested implementation sketch: This can also be done when the form is syntax: (map (lambda (thunk) (either-guard pred? (thunk))) list-of-thunks) Your alternative would be: (map (lambda (thunk) (exception->either pred? thunk)) list-of-thunks) The advantage of the syntactic form 'either-guard' is that it makes life for the optimizer much simpler. See Dybvig's critic about 'call-with-values' here: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.69.5878&rep=rep1&type=pdf. Moreover, the special form seems to be much more convenient for most use cases/users. If Scheme were really a simplistic language, not even 'if' would be a special form but would take two thunks for the consequent and the alternate. Luckily, this is not the case so using 'if' does not need a lot of boilerplate lambdas and the implementation has much better chances to optimize 'if'. I think the same arguments apply to 'either-guard'. Unrelated to this, I still consider the name 'exception->either' a bad grammatical abuse of the informal language by which we name identifiers. The name would be good for a procedure taking exception object (like R6RS's condition objects or Java's exception objects) taking their payload and wrapping them in an Either. In our case, however, we are not even remotely just transforming data. We are changing the control flow. > (define (exception->either pred thunk) > (with-exception-handler > (lambda (e) > (if (pred e) > (left e) > (raise-continuable e))) > (lambda () > (right (thunk))))) Don't use 'with-exception-handler'. Use 'guard', which is fool-proof. For non-continuable exceptions, the exception handler of 'with-exception-handler' mustn't return (or another exception is raised). So your code never yields '(left e)' to the enclosing continuation. The following version should work (apart from the bad name and apart from that I still think a syntactic form is superior): (define (exception->either pred thunk) (guard (e ((pred e) (left e))) (right (thunk)))) >> PS: The "an error is signaled" error is still in the current draft. > > > I do not think it is worthwhile to devote a dedicated predicate to catching exceptions of this type, and I think it would set a bad example. However, "an error satisfying error-object?" works for me, and I have changed the phrase both here and in SRFI 186. That's slightly better but it doesn't solve the fundamental problem, namely that you want an error being signaled (in the technical sense) in a situation (programming error), in which R7RS-small does not signal errors but encourages implementations to report the error, which could happen through signaling an error but could also happen through other means. There's no much point in catching a programmer's error with 'guard' or 'with-exception-handler' as no one expects such an error being triggered in a correct program. We really want something roughly like C's assert semantics here, which is what happens in the rest of the language. Consider: (cond ((a => x)) It is an error if 'x' does not evaluate to a procedure, but R7RS-small does not force the implementation to signal an error, which can necessarily be caught through 'with-exception-handler'. 'maybe-if' is not much different to 'cond': It is a special form taking an argument, which must evaluate to an object of a specific type (procedure or Maybe). Another big problem with the current specification of 'maybe-if' is that it hinders optimizations: (define (foo bla) (let ((x (maybe-if bla a b))) ...)) If 'maybe-if' were specified like 'cond' (in that no error has to be signaled in the technical sense), in an aggressively optimizing mode an implementation can assume that 'bla' is a Maybe. This allows type inference in the body of 'foo' but also in contexts, in which 'foo' is being called. This type inference is important for fast code (allowing to represent the relevant objects as "unbox" values in machine code, etc.). Now with your specification, the implementation's optimizer cannot assume that 'bla' is a Maybe because there will be an observable side effect when 'bla' is not a Maybe. All-in-all, there seem to be zero benefits for forcing the implementation to signal an error in the technical sense, but a number of drawbacks. Marc