unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* GNU Guile 2.9.5 Released [beta]
@ 2019-11-22 15:22 Andy Wingo
  2019-11-22 16:01 ` tomas
  2019-12-01 20:41 ` Chris Vine
  0 siblings, 2 replies; 8+ messages in thread
From: Andy Wingo @ 2019-11-22 15:22 UTC (permalink / raw)
  To: guile-users; +Cc: guile-sources, guile-devel

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-guile@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 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.

** More optimization for unexported definitions at -O3

There is a new optimization pass, `seal-private-bindings', which is
enabled at the new optimization level -O3.  (Prior to this change, -O3
was the same as -O2.)  With this pass, private declarative bindings
aren't available for access from the first-class module reflection API.
This allows for better optimization.

** Better optimization for unboxed int/float conversions

This improves optimization for "exact->inexact" and integer operands to
floating-point ops.

** Define top-level bindings for aux syntax: `else', `=>', `...', `_'

These auxiliary syntax definitions are specified to be defined in the
R6RS and the R7RS.  They were previously unbound, even in the R6RS
modules.  This change is not anticipated to cause any incompatibility
with existing Guile code, and improves things for R6RS and R7RS users.

** Conventional gettext alias is now `G_'

Related to the last point, since the "Fix literal matching for
module-bound literals" change in the 2.2 series, it was no longer
possible to use the conventional `_' binding as an alias for `gettext',
because a local `_' definition would prevent `_' from being recognized
as auxiliary syntax for `match', `syntax-rules', and similar.  The new
recommended conventional alias for `gettext' is `G_'.

** Add --r6rs command-line option

The new `install-r6rs!' procedure adapts Guile's defaults to be more
R6RS-compatible.  This procedure is called if the user passes `--r6rs'
as a command-line argument.  See "R6RS Incompatibilities" in the manual,
for full details.

** Add support for R7RS

Thanks to Göran Weinholt and OKUMURA Yuki, Guile now implements the R7RS
modules.  As the R7RS library syntax is a subset of R6RS, to use R7RS
you just `(import (scheme base))' and off you go.  As with R6RS also,
there are some small lexical incompatibilities regarding hex escapes;
see "R6RS Support" in the manual, for full details.

Also as with R6RS, there is an `install-r7rs!' procedure and a `--r7rs'
command-line option.

* New deprecations

** The two-argument form of `record-constructor'

Calling `record-constructor' with two arguments (the record type and a
list of field names) is deprecated.  Instead, call with just one
argument, and provide a wrapper around that constructor if needed.


Cumulative changes in the beta 2.9.x series (since the stable 2.2 series):

* Notable changes

** Just-in-time code generation

Guile programs now run up to 4 times faster, relative to Guile 2.2,
thanks to just-in-time (JIT) native code generation.  Notably, this
brings the performance of "eval" as written in Scheme back to the level
of "eval" written in C, as in the days of Guile 1.8.

See "Just-In-Time Native Code" in the manual, for more information.  JIT
compilation will be enabled automatically and transparently.  To disable
JIT compilation, configure Guile with `--enable-jit=no' or
`--disable-jit'.  The default is `--enable-jit=auto', which enables the
JIT if it is available.  See `./configure --help' for more.

JIT compilation is enabled by default on x86-64, i686, ARMv7, and
AArch64 targets.

** Lower-level bytecode

Relative to the virtual machine in Guile 2.2, Guile's VM instruction set
is now more low-level.  This allows it to express more advanced
optimizations, for example type check elision or integer
devirtualization, and makes the task of JIT code generation easier.

Note that this change can mean that for a given function, the
corresponding number of instructions in Guile 3.0 may be higher than
Guile 2.2, which can lead to slowdowns when the function is interpreted.
We hope that JIT compilation more than makes up for this slight
slowdown.

** Interleaved internal definitions and expressions allowed

It used to be that internal definitions had to precede all expressions
in their bodies.  This restriction has been relaxed.  If an expression
precedes an internal definition, it is treated as if it were a
definition of an unreferenced variable.  For example, the expression
`(foo)' transforms to the equivalent of `(define _ (begin (foo) #f))',
if it precedes other definitions.

This change improves the readability of Guile programs, as it used to be
that program indentation tended to increase needlessly to allow nested
`let' and `letrec' to re-establish definition contexts after initial
expressions, for example for type-checks on procedure arguments.

** Optimization of top-level bindings within a compilation unit

At optimization level 2 and above, Guile's compiler is now allowed to
inline top-level definitions within a compilation unit.  See
"Declarative Modules" in the manual, for full details.  This change can
improve the performance of programs with many small top-level
definitions by quite a bit!

** By default, GOOPS classes are not redefinable

It used to be that all GOOPS classes were redefinable, at least in
theory.  This facility was supported by an indirection in all "struct"
instances, even though only a subset of structs would need redefinition.
We wanted to remove this indirection, in order to speed up Guile
records, allow immutable Guile records to eventually be described by
classes, and allow for some optimizations in core GOOPS classes that
shouldn't be redefined anyway.

Thus in GOOPS now there are classes that are redefinable and classes
that aren't.  By default, classes created with GOOPS are not
redefinable.  To make a class redefinable, it should be an instance of
`<redefinable-class>'.  See "Redefining a Class" in the manual for more
information.

* New deprecations

** scm_t_uint8, etc deprecated in favor of C99 stdint.h

It used to be that Guile defined its own `scm_t_uint8' because C99
`uint8_t' wasn't widely enough available.  Now Guile finally made the
change to use C99 types, both internally and in Guile's public headers.

Note that this also applies to SCM_T_UINT8_MAX, SCM_T_INT8_MIN, for intN
and uintN for N in 8, 16, 32, and 64.  Guile also now uses ptrdiff_t
instead of scm_t_ptrdiff, and similarly for intmax_t, uintmax_t,
intptr_t, and uintptr_t.

* Incompatible changes

** All deprecated code removed

All code deprecated in Guile 2.2 has been removed.  See older NEWS, and
check that your programs can compile without linker warnings and run
without runtime warnings.  See "Deprecation" in the manual.

In particular, the function `scm_generalized_vector_get_handle' which
was deprecated in 2.0.9 but remained in 2.2, has now finally been
removed. As a replacement, use `scm_array_get_handle' to get a handle
and `scm_array_handle_rank' to check the rank.

** Remove "self" field from vtables and "redefined" field from classes

These fields were used as part of the machinery for class redefinition
and is no longer needed.

** VM hook manipulation simplified

The low-level mechanism to instrument a running virtual machine for
debugging and tracing has been simplified.  See "VM Hooks" in the
manual, for more.

* Changes to the distribution

** New effective version

The "effective version" of Guile is now 3.0, which allows parallel
installation with other effective versions (for example, the older Guile
2.2).  See "Parallel Installations" in the manual for full details.
Notably, the `pkg-config' file is now `guile-3.0'.



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2019-11-22 15:22 GNU Guile 2.9.5 Released [beta] Andy Wingo
@ 2019-11-22 16:01 ` tomas
  2019-12-01 20:41 ` Chris Vine
  1 sibling, 0 replies; 8+ messages in thread
From: tomas @ 2019-11-22 16:01 UTC (permalink / raw)
  To: guile-devel

[-- Attachment #1: Type: text/plain, Size: 368 bytes --]

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2019-11-22 15:22 GNU Guile 2.9.5 Released [beta] Andy Wingo
  2019-11-22 16:01 ` tomas
@ 2019-12-01 20:41 ` Chris Vine
  2020-01-05 20:15   ` Andy Wingo
  1 sibling, 1 reply; 8+ messages in thread
From: Chris Vine @ 2019-12-01 20:41 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-users, guile-devel

On Fri, 22 Nov 2019 16:22:39 +0100
Andy Wingo <wingo@pobox.com> 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 the guard body to do so?
I think this could be achieved simply by executing
with-exception-handler in the guard0 syntactic form with #unwind set to
true.

Chris



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2019-12-01 20:41 ` Chris Vine
@ 2020-01-05 20:15   ` Andy Wingo
  2020-01-05 23:26     ` Chris Vine
  0 siblings, 1 reply; 8+ messages in thread
From: Andy Wingo @ 2020-01-05 20:15 UTC (permalink / raw)
  To: Chris Vine; +Cc: Andy Wingo, guile-users, guile-devel

On Sun 01 Dec 2019 21:41, Chris Vine <vine35792468@gmail.com> 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



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2020-01-05 20:15   ` Andy Wingo
@ 2020-01-05 23:26     ` Chris Vine
  2020-01-06 20:34       ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: Chris Vine @ 2020-01-05 23:26 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-users, guile-devel

On Sun, 05 Jan 2020 21:15:52 +0100
Andy Wingo <wingo@pobox.com> wrote:
> On Sun 01 Dec 2019 21:41, Chris Vine <vine35792468@gmail.com> 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?

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.

That is how I think it should be done, but it is inconsistent with the
specification for R6RS/R7RS 'guard'.  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.  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.

So it appears that the choice for 'guard' in guile is between adopting
the approach of my 'try' macro (unwind and re-raise if necessary with
'raise'), or to do what you propose and not to unwind the stack when
evaluating the cond clause conditionals, and if no cond conditional
matches to re-raise with 'raise-continuable', or if one matches to
unwind and evaluate the cond consequent in the dynamic environment of
'guard'.

Or put another way, the choice is either to fail to comply with
R6RS/R7RS by re-raising with 'raise' instead of 'raise-continuable' (my
'try' macro), or to fail to comply with the requirement to evaluate the
cond clause conditionals in the dynamic environment of the call to
'guard' (your proposal).  Naturally I prefer the first approach, but
others may well disagree.

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?  The only place where
I have seen restarts used is in building REPLs.  First, most other
experience leads me to believe that the place to decide whether recovery
is possible (and how to do it) in the event of an exceptional situation
arising is at the site of the exceptional situation, not somewhere up
the stack which varies dynamically and could be anywhere.  Secondly,
continuable exceptions can break resource management using dynamic
winds or re-throws - for example once you close a port on exit it is
closed and cannot be re-opened on re-entry without loss of information,
if it can be re-opened at all.  I see little practical use for them.

Chris



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2020-01-05 23:26     ` Chris Vine
@ 2020-01-06 20:34       ` Andy Wingo
  2020-01-06 23:14         ` Chris Vine
  0 siblings, 1 reply; 8+ messages in thread
From: Andy Wingo @ 2020-01-06 20:34 UTC (permalink / raw)
  To: Chris Vine; +Cc: Andy Wingo, guile-users, guile-devel

On Mon 06 Jan 2020 00:26, Chris Vine <vine35792468@gmail.com> 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



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2020-01-06 20:34       ` Andy Wingo
@ 2020-01-06 23:14         ` Chris Vine
  2020-01-07 21:53           ` Andy Wingo
  0 siblings, 1 reply; 8+ messages in thread
From: Chris Vine @ 2020-01-06 23:14 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-user, guile-devel

On Mon, 06 Jan 2020 21:34:59 +0100
Andy Wingo <wingo@pobox.com> wrote:
> On Mon 06 Jan 2020 00:26, Chris Vine <vine35792468@gmail.com> 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



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GNU Guile 2.9.5 Released [beta]
  2020-01-06 23:14         ` Chris Vine
@ 2020-01-07 21:53           ` Andy Wingo
  0 siblings, 0 replies; 8+ messages in thread
From: Andy Wingo @ 2020-01-07 21:53 UTC (permalink / raw)
  To: Chris Vine; +Cc: guile-user, guile-devel

On Tue 07 Jan 2020 00:14, Chris Vine <vine35792468@gmail.com> 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



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-01-07 21:53 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-22 15:22 GNU Guile 2.9.5 Released [beta] Andy Wingo
2019-11-22 16:01 ` tomas
2019-12-01 20:41 ` Chris Vine
2020-01-05 20:15   ` Andy Wingo
2020-01-05 23:26     ` Chris Vine
2020-01-06 20:34       ` Andy Wingo
2020-01-06 23:14         ` Chris Vine
2020-01-07 21:53           ` Andy Wingo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).