#scheme #guile #gambit #kawa
#схема #хитрость #гамбит #кава
Вопрос:
Я пытаюсь создать код, который вычисляет выражение и возвращает ошибку в виде строки для ошибки:
(cond-expand
(gambit)
(gauche)
(kawa)
(guile
(import (rnrs base)
(rnrs exceptions)
(rnrs conditions))
(define (error-object-message cond)
(condition-message cond))))
(define (evaluate expr env)
(call-with-current-continuation
(lambda (exit)
(with-exception-handler
(lambda (e)
(exit (error-object-message e)))
(lambda ()
(eval expr env))))))
;; trigger error
(display (evaluate 'xxx (interaction-environment)))
(newline)
У меня есть
- Хитрое сообщение
Unbound variable: ~S
как получить фактическое сообщение об ошибке, а не шаблон? - Исключение Kawa:
Argument #1 'unbound location: xxx' to 'error-object-message' has wrong type (gnu.mapping.UnboundLocationException) (gnu.mapping.UnboundLocationException cannot be cast to kawa.lang.NamedException)
- Gauche дамп ядра
- Гамбит зависает
ПРИМЕЧАНИЕ: это часть REPL, которую я тестирую во всех реализациях Scheme, которые у меня есть в моей системе. Это почти работает, оно может запускаться само по себе, но я хотел бы показывать правильное сообщение об ошибке при возникновении исключения, вместо выхода из REPL.
Ответ №1:
Причина, по которой вы получаете бесконечный цикл с Gambit, заключается в том, что переменная не xxx
привязана, поэтому обработчик исключений (lambda (e) (exit (error-object-message e)))
вызывается с unbound-global-exception
объектом, и это вызывает error-object-message
вызов, но параметр не является error-object
(который специфичен для исключений, вызванных вызовом error
процедуры), поэтому возникает type-exception
объект, который вызывает то же самоевызываемый обработчик исключений и так далее до бесконечности.
Если вы хотите, чтобы обработка исключений «всплывала» из текущего обработчика исключений, используйте with-exception-catcher
вместо with-exception-handler
. Это позволит избежать бесконечного цикла.
Преобразование объекта исключения в строку может быть выполнено в Gambit таким образом:
(define (exception->string exc)
(with-output-to-string
(lambda ()
(display-exception exc))))
Это работает для error-objects
и других видов исключений, а также для любого объекта, не являющегося исключением.
Это более переносимое решение (поверхностно протестировано):
(import (scheme base)
(scheme r5rs))
(cond-expand
((or lips kawa gauche)
(define (exception->string exc)
(error-object-message exc)))
(gambit
(define (exception->string exc)
(with-output-to-string
(lambda ()
(display-exception exc)))))
(guile
(import (rnrs base)
(rnrs exceptions)
(rnrs conditions))
(define (exception->string exc)
(condition-message exc))))
(define (evaluate expr env)
(call-with-current-continuation
(lambda (exit)
(with-exception-handler
(lambda (e)
(exit (exception->string e)))
(lambda ()
(eval expr env))))))
(display (evaluate 'xxx (interaction-environment)))
(newline)
Ответ №2:
Gauche дамп ядра
Упс. Конечно, это не идеально, но это можно объяснить.
- По умолчанию Gauche — это
with-exception-handler
SRFI-18, а не R7RS, по исторической причине. Это означает, что обработчик исключений вызывается в динамической среде, в которой возникает исключение, включая настройки обработчика исключений. Если в обработчике исключений возникает исключение, вызывается тот же обработчик исключений, вызывающий неограниченную рекурсию. По-видимому, среда выполнения Gauche съедает стек C или что-то в этом роде. error-object-message
не определено в пространстве имен Gauche по умолчанию. Так что это в первую очередь вызывает исключение.
Добавить
(import (scheme base) (scheme write) (scheme r5rs))
в начале кода программа запускается в привязках R7RS. Тогда вы получите:
unbound variable: xxx
На самом деле, ваш код не является допустимой программой R7RS (которая должна начинаться хотя бы с одного import
объявления), поэтому может произойти все, что угодно, в зависимости от интерпретации несоответствующего кода по умолчанию в реализации.
[Редактировать] ИМХО, with-exception-handler
следует рассматривать как конструкцию самого низкого уровня, на которой построены простые в использовании утилиты, и, следовательно, их следует использовать с особой осторожностью. В общем случае использования guard
обеспечивает хорошую абстракцию.
Ответ №3:
Для Kawa:
(define (exception->string exc)
(call-with-port (open-output-string)
(lambda (port)
(display exc port)
(get-output-string port)))))
Это преобразует исключение в строку и получит сообщение об ошибке