Как определить исключения в схеме, используя динамическую переменную и аварийный контроль? - PullRequest
0 голосов
/ 23 октября 2018

У меня проблемы с реализацией исключений (handle и raise) с использованием динамически изменяемой переменной и abort.

Этот вопрос возник из чтения статьи Синтаксическая теория динамического связывания, раздел 6, рисунок 7.

Кажется, что я попытался сработать правильно, создав исключение внутри блока try, а также исключение внутри блока try внутри блока try.

То, что не работает правильно, вызывает исключение из обработчика.В этом случае он должен прерваться до следующей попытки.

Здесь вы можете увидеть мою работу в виде схемы ракетки вместе с двумя тестовыми программами.test-1 работает, а test-2 не работает.

#lang racket

;; A Syntactic Theory of Dynamic Binding - Luc Moreau
;; https://link.springer.com/content/pdf/10.1007%2FBFb0030637.pdf

(require racket/control)

(define x_ed (make-parameter 'x_ed))

; NOTE: (abort ..) is the same as (shift k ..) where you don't use k
; NOTE: in (handle f M) we call f the handler and M the try block

; v1
;(define-syntax handle
;  (syntax-rules ()
;    ((handle f M)
;     (parameterize ((x_ed (lambda (v) (abort (f v))))) M))))
; v2
;(define-syntax handle
;  (syntax-rules ()
;    ((handle f M)
;     (reset (parameterize ((x_ed (lambda (v) (abort (f v))))) M)))))
; v3
;(define-syntax handle
;  (syntax-rules ()
;    ((handle f M)
;     (parameterize ((x_ed (lambda (v) (abort (f v))))) (reset M)))))
; v4
(define-syntax handle
  (syntax-rules ()
    ((handle f M)
     (let ((old-x_ed (x_ed)))
       (parameterize ((x_ed (lambda (v)
                              (abort (parameterize ((x_ed old-x_ed))
                                       (f v))))))
         (reset M))))))
(define-syntax raise
  (syntax-rules ()
    ((raise v) ((x_ed) v))))

(define (print x) (write x) (newline))

(define (test-1)
  (print "level-1 open")
  (handle (lambda (v)
            (print "level-1 caught"))
          (begin
            (print "level-2 open")
            (handle (lambda (v)
                      (print "level-2 caught"))
                    (begin
                      (print "level-3 open")
                      (raise #t)
                      (print "level-3 close")))
            (print "level-2 close")))
  (print "level-1 close"))

(define (test-2)
  (print "level-1 open")
  (handle (lambda (v)
            (print "level-1 caught"))
          (begin
            (print "level-2 open")
            (handle (lambda (v)
                      (print "level-2 caught")
                      (raise #t))
                    (begin
                      (print "level-3 open")
                      (raise #t)
                      (print "level-3 close")))
            (print "level-2 close")))
  (print "level-1 close"))

;v1
;> (test-1)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"

;v2 and v3
;> (test-1)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"
;"level-2 close"
;"level-1 close"

;v2 and v3
;> (test-2)
;...
;"level-2 caught"
;"level-2 caught"
; infinite loop

;v4
;> (test-2)
;"level-1 open"
;"level-2 open"
;"level-3 open"
;"level-2 caught"
;"level-1 caught"
;"level-2 close" <--- we don't want this to happen
;"level-1 close"

Спасибо.


Мне удалось придумать эту рабочую версию благодаря ответу:

(define-syntax handle
  (syntax-rules ()
    ((handle f M)
     (prompt0
      (parameterize ((x_ed (lambda (v)
                             (control0 k (f v)))))
        M)))))

1 Ответ

0 голосов
/ 24 октября 2018

( Редактировать : я ошибся из-за того, что смог сделать более компактную реализацию, используя специальные операторы управления. Можно заставить обработчик работать в хвостовом положении относительно формы handle, но я не знаю ни одного способа оценить тело в положении хвоста.)

Прежде всего, вы специально пытаетесь реализовать обработку исключений, когда тело формы handleнаходится в хвостовой позиции относительно самой формы handle?Если нет, то есть гораздо более простые способы реализации обработки исключений в терминах простых продолжений экранирования.

Если вы действительно хотите реализовать исключение "безопасный для пространства" или "правильно заданный рекурсивный"Обращаясь, однако, к прочтению.

Сложность реализации handle безопасным для пространства способом состоит в том, чтобы избежать вставки дополнительного кадра стека в «исключение не возбуждено»путь, вам нужна возможность размотать стек и возобновить оценку с выражением в этом контексте.Или, эквивалентно, возобновите оценку, вызвав процедуру в этом контексте.Это отличается от того, что обеспечивает call/cc;он позволяет только разматывать стек и затем сразу возвращать значение в этот контекст.

Вы можете смоделировать дополнительную мощность с помощью call/cc за счет вставки дополнительного кадра стека (таким образом, телоне находится в хвостовой позиции):

;; call/cc :       ((Any      -> None) -> Any) -> Any

;; call/cc/apply : (((-> Any) -> None) -> Any) -> Any
(define (call/cc/apply proc)
  ((call/cc (lambda (k) (let ([v (proc k)]) (lambda () v))))))

Дополнительный кадр стека получается в результате применения выражения call/cc.

Можете ли вы устранить необходимость в этомдополнительный кадр стека?Да!Но не с shift и reset.

Проблема, с которой вы столкнулись, заключается в том, что (abort e) (где abort соответствует оператору Фелляйзена и A Хиба) не совпадает с (shift _ e).Если вы посмотрите на документы для сдвига и сброса , вы увидите следующие правила сокращения:

(reset val) => val
(reset E[(shift k expr)]) => (reset ((lambda (k) expr)
                                     (lambda (v) (reset E[v]))))
  ; where E has no reset

То есть shift не удалить его разграничение reset, и этот упрямый reset - это то, что мешает вам выпрыгнуть из вашего обработчика уровня 2 прямо в ваш обработчик уровня 1 без запуска (print "level-2 close").Вам нужно выбрать разделитель и управляющий оператор, который позволит вам удалить разделитель.

Вы не можете сделать это с reset и shift.
Вы не можете сделать это с prompt и control.
Вы можете сделать это с prompt0 и control0.
Вы можете сделать это с % и fcontrol (и правым обработчиком).
И, конечно, вы можете сделатьэто с call-with-continuation-prompt и abort-current-continuation (и правым обработчиком).

...