guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: GNU Guile 2.9.5 Released [beta]


From: Andy Wingo
Subject: Re: GNU Guile 2.9.5 Released [beta]
Date: Sun, 05 Jan 2020 21:15:52 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (gnu/linux)

On Sun 01 Dec 2019 21:41, Chris Vine <address@hidden> 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]