Как отобразить сообщение об ошибке из eval в схеме?

#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)))))
 

Это преобразует исключение в строку и получит сообщение об ошибке