Re: GNU Guile 2.9.5 Released [beta]
On Tue 07 Jan 2020 00:14, Chris Vine writes: > I wonder if it would surprise the programmer to have the cond > conditionals evaluated in a different dynamic environment from the one > in which the cond consequential is evaluated where there is a > conditional which is true. I entirely agree it's not ideal and can be surprising! I am not sure that there is an "ideal" here though; with-exception-handler is wonderfully expressive but can be verbose, guard is a pleasant abbreviation but how to deal with re-raising from the original context? In the end, "guard" is just a macro over a more general facility. But it's a macro that we expect people to use, and to cover the common case. To that end I think we should make it cheap and avoid rewinding while also preserving the nice characteristic of evaluating cond consequents in the continuation of the "guard" itself. Andy
Re: GNU Guile 2.9.5 Released [beta]
On Mon, 06 Jan 2020 21:34:59 +0100 Andy Wingo wrote: > On Mon 06 Jan 2020 00:26, Chris Vine writes: > > I have a 'try' macro which adopts the approach that if an exception > > arises, the macro unwinds from the dynamic environment of the code > > where the exception arose to the dynamic environment of the call to > > 'try', evaluates the cond clauses in that environment, and then if no > > cond clause matches re-raises the exception in that environment with > > 'raise' (rather than 'raise-continuable'). In other words, it does > > stack unwinding in the same way as exception implementations in almost > > all other mainstream languages which use exceptions. It would be > > trivial to implement this with guile-3.0's with-exception-handler with > > its unwind? argument set to true. > > I am not sure this really matches with this use case: > > (define (call-with-backtrace thunk) > (call/ec > (lambda (ret) >(with-exception-handler > (lambda (exn) >(show-backtrace exn) ;; placeholder >(ret)) > thunk > > (define (false-on-file-errors thunk) > (call/ec > (lambda (ret) >(with-exception-handler > (lambda (exn) >(if (file-error? exn) >(ret #f) >(raise-continuable exn))) > thunk > > (define (foo f) > (call-with-backtrace > (lambda () >(false-on-file-errors f > > > If there's an error while invoking `f' that's not a file error, you want > to have remained in the context of the error so you can show a full > backtrace. To my mind this is central to the exception handler design. > So far so good I think. > > If I change the implementation of `false-on-file-errors' to be: > > (define (false-on-file-errors thunk) > (guard (exn ((file-error? exn) #f)) > (thunk))) > > I think this change should preserve the not-unwinding environment that > `call-with-backtrace' expects. Good point. My approach does provide the programmer with less conveyed stack information after the re-raise of an unhandled exception, requiring more manual intervention to recover the information when debugging the exception. Before you suggested it I had not previously considered your proposal. It may turn out to be the optimum solution, but I wonder if it would surprise the programmer to have the cond conditionals evaluated in a different dynamic environment from the one in which the cond consequential is evaluated where there is a conditional which is true. But I am not sure if that is of any importance. Chris
Re: GNU Guile 2.9.5 Released [beta]
On Mon 06 Jan 2020 00:26, Chris Vine writes: > I have a 'try' macro which adopts the approach that if an exception > arises, the macro unwinds from the dynamic environment of the code > where the exception arose to the dynamic environment of the call to > 'try', evaluates the cond clauses in that environment, and then if no > cond clause matches re-raises the exception in that environment with > 'raise' (rather than 'raise-continuable'). In other words, it does > stack unwinding in the same way as exception implementations in almost > all other mainstream languages which use exceptions. It would be > trivial to implement this with guile-3.0's with-exception-handler with > its unwind? argument set to true. I am not sure this really matches with this use case: (define (call-with-backtrace thunk) (call/ec (lambda (ret) (with-exception-handler (lambda (exn) (show-backtrace exn) ;; placeholder (ret)) thunk (define (false-on-file-errors thunk) (call/ec (lambda (ret) (with-exception-handler (lambda (exn) (if (file-error? exn) (ret #f) (raise-continuable exn))) thunk (define (foo f) (call-with-backtrace (lambda () (false-on-file-errors f If there's an error while invoking `f' that's not a file error, you want to have remained in the context of the error so you can show a full backtrace. To my mind this is central to the exception handler design. So far so good I think. If I change the implementation of `false-on-file-errors' to be: (define (false-on-file-errors thunk) (guard (exn ((file-error? exn) #f)) (thunk))) I think this change should preserve the not-unwinding environment that `call-with-backtrace' expects. > On the other hand, as you say it does not seem feasible to implement > in guile the R6RS/R7RS requirement to unwind to the environment of the > call to 'guard' when evaluating the cond clauses, and then return to > the environment of the original exception in order to re-raise if no > cond clause matches. It's feasible, just not a good idea IMO. The problem is that call/cc is quite expensive. Additionally that it captures the whole state of the current thread, so a fiber (github.com/wingo/fibers) with a `guard' may error if it is preempted and migrated to a different CPU. > Furthermore such a return is only relevant if the exception is to be > re-raised with 'raise-continuable' instead of 'raise': it is pointless > if the exception is re-raised with 'raise' because with 'raise' you > can never get back there again. FWIW I am not sure how raise-continuable will be used but it's a fairly straightforward thing implementation-wise that doesn't bother me. > I am somewhat influenced by my view of 'raise-continuable'. I don't > like it - how often does anyone use continuable exceptions, which seem > to be a reimplementation of common lisp restarts? I am not sure that they are restarts. A restart to my mind is more like: (define (with-restart name thunk) (let lp () (define tag (make-prompt-tag)) (call-with-prompt tag (lambda () (parameterize ((current-restarts (acons name tag (current-restart (thunk))) (lambda (k) (lp) (define (invoke-restart-by-name name . vals) (match (assoc name (current-restarts)) ((name . tag) (apply abort-to-prompt tag vals So you could invoke a restart within an exception handler but it has nothing to do with whether raise or raise-continuable was used. The continuation captured by the equivalent of common lisp's `restart-case' isn't the continuation that raises the error. Regards, Andy
Re: GNU Guile 2.9.5 Released [beta]
On Sun 01 Dec 2019 21:41, Chris Vine writes: > Is this rewrite, and the new with-exception-handler procedure, an > opportunity to think about standardization of guile's implementation of > the R6RS/R7RS 'guard' form, or at least think about what is wanted for > 'guard'? > > The formal semantics (including specimen implementation) of 'guard' for > R6RS with the corrigendum to §7.1 of the standard library at > http://www.r6rs.org/r6rs-errata.html, and for R7RS without corrigendum > (at §4.2.7 and §7.3, page 72 of the standard), is: > > (i) to evaluate the guard body within a block with its own continuation > (as constructed by call/cc); > > (ii) if an exception is thrown, evaluate the handler (and its cond > clauses) in the dynamic context of the original caller of 'guard' via > that continuation; > > (iii) if no matching cond clause and no else clause is found, return to > the dynamic environment of the original 'raise' and re-raise the > exception with 'raise-continuable', even for non-continuable > exceptions. > > If a fully conforming R6RS/R7RS implementation runs this code: > > (guard (exn [(equal? exn 5) #f]) > (guard (exn [(equal? exn 6) 'never-reached]) > (dynamic-wind > (lambda () (display "in") (newline)) > (lambda () (raise 5)) > (lambda () (display "out") (newline) > > the code evaluates to #f and should print this: > > in > out > in > out > > In chez scheme it does so. In most other implementations (including > guile and racket) it seems to print: > > in > out I really think the standards messed up regarding the specification of "guard": http://scheme-reports.org/mail/scheme-reports/msg03247.html But those ships have sailed and are now lost at sea. Guile currently has two separate implementations of "guard" for SRFI-34 (used by R7RS) and R6RS. It would seem that besides not respecting the specification, the R6RS one is broken, as it expects the "cond" clauses to evaluate to a single value. For SRFI-34 (and R7RS), after the exception refactor, I did a re-write to give a shot at implementing the specified behavior. It works with a caveat: because it uses delimited continuations as the rewind mechanism, and Guile has a limitation that some delimited continuations can't be rewound (if the continuation bounces through C), then re-raising the exception fails because the context can't be rewound. This can cause previously working programs to break! Which makes me think, if call/cc (rather than call-with-prompt / abort-to-prompt) is necessary to implement "guard", we are in a bad place and we should specify something else. I have long thought that the right thing to do is this: we evaluate the "cond" tests in the dynamic environment of the "raise". Then if a test succeeds, we unwind and run the corresponding consequent. That way there's no rewinding. Here's an implementation: (define-syntax guard (syntax-rules (else) ((guard (var (t e e* ...) ...) body body* ...) (let ((tag (make-prompt-tag))) (call-with-prompt tag (lambda () (with-exception-handler (lambda (var) (cond (t (abort-to-prompt tag (lambda () e e* ...))) ...) (raise var)) (lambda () body body* ...))) (lambda (k thunk) (thunk))) Though I think it might be reasonable to use "raise-continuable" instead of "raise" if nothing matches. WDYT? Andy
Re: GNU Guile 2.9.5 Released [beta]
On Fri, 22 Nov 2019 16:22:39 +0100 Andy Wingo wrote: > We are pleased to announce GNU Guile release 2.9.5. This is the fifth > pre-release of what will eventually become the 3.0 release series. [snip] > ** Reimplementation of exceptions > > Since Guile's origins 25 years ago, `throw' and `catch' have been the > primary exception-handling primitives. However these primitives have > two problems. One is that it's hard to handle exceptions in a > structured way using `catch'. Few people remember what the > corresponding `key' and `args' are that an exception handler would see > in response to a call to `error', for example. In practice, this > results in more generic catch-all exception handling than one might > like. > > The other problem is that `throw', `catch', and especially > `with-throw-handler' are quite unlike what the rest of the Scheme world > uses. R6RS and R7RS, for example, have mostly converged on > SRFI-34-style `with-exception-handler' and `raise' primitives, and > encourage the use of SRFI-35-style structured exception objects to > describe the error. Guile's R6RS layer incorporates an adapter between > `throw'/`catch' and structured exception handling, but it didn't apply > to SRFI-34/SRFI-35, and we would have to duplicate it for R7RS. > > In light of these considerations, Guile has now changed to make > `with-exception-handler' and `raise-exception' its primitives for > exception handling and defined a hierarchy of R6RS-style exception types > in its core. SRFI-34/35, R6RS, and the exception-handling components of > SRFI-18 (threads) have been re-implemented in terms of this core > functionality. There is also a a compatibility layer that makes it so > that exceptions originating in `throw' can be handled by > `with-exception-hander', and vice-versa for `raise-exception' and > `catch'. > > Generally speaking, users will see no difference. The one significant > difference is that users of SRFI-34 will see more exceptions flowing > through their `with-exception-handler'/`guard' forms, because whereas > before they would only see exceptions thrown by SRFI-34, now they will > see exceptions thrown by R6RS, R7RS, or indeed `throw'. > > Guile's situation is transitional. Most exceptions are still signalled > via `throw'. These will probably migrate over time to > `raise-exception', while preserving compatibility of course. > > See "Exceptions" in the manual, for full details on the new API. Is this rewrite, and the new with-exception-handler procedure, an opportunity to think about standardization of guile's implementation of the R6RS/R7RS 'guard' form, or at least think about what is wanted for 'guard'? The formal semantics (including specimen implementation) of 'guard' for R6RS with the corrigendum to §7.1 of the standard library at http://www.r6rs.org/r6rs-errata.html, and for R7RS without corrigendum (at §4.2.7 and §7.3, page 72 of the standard), is: (i) to evaluate the guard body within a block with its own continuation (as constructed by call/cc); (ii) if an exception is thrown, evaluate the handler (and its cond clauses) in the dynamic context of the original caller of 'guard' via that continuation; (iii) if no matching cond clause and no else clause is found, return to the dynamic environment of the original 'raise' and re-raise the exception with 'raise-continuable', even for non-continuable exceptions. If a fully conforming R6RS/R7RS implementation runs this code: (guard (exn [(equal? exn 5) #f]) (guard (exn [(equal? exn 6) 'never-reached]) (dynamic-wind (lambda () (display "in") (newline)) (lambda () (raise 5)) (lambda () (display "out") (newline) the code evaluates to #f and should print this: in out in out In chez scheme it does so. In most other implementations (including guile and racket) it seems to print: in out Guile 2.9.5 appears to implement 'guard' this way: (i) to evaluate the guard body within a block with its own continuation (as constructed by call/ec); (ii) if an exception is thrown, evaluate the handler (and its cond clauses) in the dynamic environment of the guard body within which the raise occurred (apart from the current exception handler which is reset); (iii) if no matching cond clause and no else clause is found, re-raise the exception with 'raise' within the dynamic context of that guard body. I don't especially like the mandated behaviour of 'guard', which seems to be intended to allow the guard form to handle continuable exceptions as continuable elsewhere in the call stack, which seems fairly pointless to me. If this is to be departed from, then how about doing what most people expect of a high-level exception form, and to unwind the stack by executing the cond clauses within the dynamic context of the caller of 'guard' (as R6RS/R7RS do), not in that of the guard body, and then if a re-throw is necessary do it with 'raise' within that context instead of returning to
Re: GNU Guile 2.9.5 Released [beta]
On Fri, Nov 22, 2019 at 04:22:39PM +0100, Andy Wingo wrote: > We are pleased to announce GNU Guile release 2.9.5. This is the fifth > pre-release of what will eventually become the 3.0 release series. Thanks! Compiling now. And oh, thank you for all the magic (and for explaining it to us mere muggles :-) Thanks also to all the other magicians here. Cheers -- t signature.asc Description: Digital signature
GNU Guile 2.9.5 Released [beta]
We are pleased to announce GNU Guile release 2.9.5. This is the fifth pre-release of what will eventually become the 3.0 release series. Compared to the current stable series (2.2.x), the future Guile 3.0 adds support for just-in-time native code generation, speeding up all Guile programs. See the NEWS extract at the end of the mail for full details. Compared to the previous prerelease (2.9.4), Guile 2.9.5 adds low-level optimizations for converting between unboxed integers and floating-point values, generates faster code at optimization level -O3, adds R7RS support, a --r6rs command-line argument, and a fresh implementation of raise-exception and with-exception-handler. We encourage you to test this release and provide feedback to guile-devel@gnu.org, and to file bugs by sending mail to bug-gu...@gnu.org. The Guile web page is located at http://gnu.org/software/guile/, and among other things, it contains a copy of the Guile manual and pointers to more resources. Guile is an implementation of the Scheme programming language, with support for many SRFIs, packaged for use in a wide variety of environments. In addition to implementing the R5RS Scheme standard, Guile includes a module system, full access to POSIX system calls, networking support, multiple threads, dynamic linking, a foreign function call interface, and powerful string processing. Guile can run interactively, as a script interpreter, and as a Scheme compiler to VM bytecode. It is also packaged as a library so that applications can easily incorporate a complete Scheme interpreter/VM. An application can use Guile as an extension language, a clean and powerful configuration language, or as multi-purpose "glue" to connect primitives provided by the application. It is easy to call Scheme code >From C code and vice versa. Applications can add new functions, data types, control structures, and even syntax to Guile, to create a domain-specific language tailored to the task at hand. Guile 2.9.5 can be installed in parallel with Guile 2.2.x; see http://www.gnu.org/software/guile/manual/html_node/Parallel-Installations.html. A more detailed NEWS summary follows these details on how to get the Guile sources. Here are the compressed sources: http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.lz (10MB) http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.xz (12MB) http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.gz (21MB) Here are the GPG detached signatures[*]: http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.lz.sig http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.xz.sig http://alpha.gnu.org/gnu/guile/guile-2.9.5.tar.gz.sig Use a mirror for higher download bandwidth: http://www.gnu.org/order/ftp.html Here are the SHA256 checksums: 0c0097092fc5b0b40c5eb97ce09bf5415b31c5f7241f4cdcb01284f81cb2f70f guile-2.9.5.tar.lz f917cb8578740887df8e0090fdecf3f06aaf60d2331067b88ff5c3bb610d69b5 guile-2.9.5.tar.xz 199c5dbba3ec4322dfd9f1e1af6c19deca77fa4a104c59f26aeea23f7640720d guile-2.9.5.tar.gz [*] Use a .sig file to verify that the corresponding file (without the .sig suffix) is intact. First, be sure to download both the .sig file and the corresponding tarball. Then, run a command like this: gpg --verify guile-2.9.5.tar.gz.sig If that command fails because you don't have the required public key, then run this command to import it: gpg --keyserver keys.gnupg.net --recv-keys 4FD4D288D445934E0A14F9A5A8803732E4436885 and rerun the 'gpg --verify' command. This release was bootstrapped with the following tools: Autoconf 2.69 Automake 1.16.1 Libtool 2.4.6 Gnulib v0.1-1157-gb03f418 Makeinfo 6.5 Changes in beta 2.9.5 (since beta 2.9.4): * Notable changes ** Record unification Guile used to have a number of implementations of structured data types in the form of "records": a core facility, SRFI-9 (records), SRFI-35 (condition types -- a form of records) and R6RS records. These facilities were not compatible, as they all were built in different ways. This had the unfortunate corollary that SRFI-35 conditions were not compatible with R6RS conditions. To fix this problem, we have now added the union of functionality from all of these record types into core records: single-inheritance subtyping, mutable and immutable fields, and so on. See "Records" in the manual, for full details. R6RS records, SRFI-9 records, and the SRFI-35 and R6RS exception types have been accordingly "rebased" on top of core records. ** Reimplementation of exceptions Since Guile's origins 25 years ago, `throw' and `catch' have been the primary exception-handling primitives. However these primitives have two problems. One is that it's hard to handle exceptions in a structured way using `catch'. Few people remember what the corresponding `key' and `args' are that an exception handler would see in response to a call to `error', for example. In practice, this results in more generic catch-all exception handling than one might like. The other