Chez Scheme: импорт макросов на верхнем уровне

Я запускаю Chez Scheme 9.5 и пытаюсь определить синтаксический преобразователь в библиотеке. Вот пример:

(library (forlib)
  (export for)
  (import (rnrs (6)))

  (define-syntax for
    (syntax-rules (in)
      [(for x in lst body1 body2 ...)
       (for-each (lambda (x) body1 body2 ...) lst)])))

Я сохраняю это в файле forlib.ss и запускаю chez из того же каталога. Затем в REPL я получаю это:

> (import (forlib))
> (for x in '(1 2 3) (display x))
Exception: invalid syntax (for x in (quote (1 2 3)) (display x))
Type (debug) to enter the debugger.

Если я изменю определение синтаксиса на

(define-syntax for
  (syntax-rules ()
    [(for x lst body1 body2 ...)
     (for-each (lambda (x) body1 body2 ...) lst)])))

(без ключевого слова in) все работает:

> (import (forlib))
> (for x '(1 2 3) (display x))
123
> _

Вернуться к старому определению с ключевым словом in. Если я помещу тестовый код в тестовый файл:

;;; test-for.ss
(import (rnrs (6))
        (forlib))

(for x in '(1 2 3) (display x))

и попытаться выполнить этот файл, результат зависит от того, как я выполняю файл. Если я запускаю эту программу, используя chez --program, она работает так, как ожидалось:

$ chez --program test-for.ss
123
$ _

Если я запускаю его с помощью chez --script, я получаю ту же ошибку, что и выше:

$ chez --script test-for.ss
Exception: invalid syntax (for x in (quote (1 2 3)) (display x)) at line 6, char 1 of test-for.ss
$ _

Это вызывает два вопроса:

  • Почему REPL и --script нормально импортируют синтаксические формы без специальных ключевых слов, но отказываются принимать синтаксические формы, в которых есть специальные ключевые слова?
  • В чем именно разница между --script и --program? В руководстве пользователя сказано, что --program означает, что содержимое файла интерпретируется как программа верхнего уровня rnrs, но ничего не говорится о семантике --script.

Наконец, чтобы довести мою путаницу до конца, если я введу приведенное выше определение синтаксиса непосредственно в REPL, тогда все будет работать так, как ожидалось:

> (define-syntax for
    (syntax-rules (in)
      [(for x in lst body1 body2 ...)
       (for-each (lambda (x) body1 body2 ...) lst)])))
> (for x in '(1 2 3) (display x))
123
> _

Так чем же отличаются в REPL преобразователи синтаксиса, импортированные из библиотеки, и преобразователи синтаксиса, определенные непосредственно в REPL?


person Norbert Zeh    schedule 13.01.2019    source источник
comment
forlib должен экспортировать как «в», так и «для»   -  person Chris Vine    schedule 20.01.2019
comment
@ChrisVine: это только часть решения. Если бы я попытался экспортировать in, как вы предлагаете, я бы получил сообщение об ошибке, потому что в forlib не определено имя для экспорта. Как указано ниже @gmw, для того, чтобы это работало, forlib необходимо создать фиктивное имя in, которое можно экспортировать.   -  person Norbert Zeh    schedule 21.01.2019
comment
Изучив это, вы правы. См. это: cisco.github.io/ChezScheme/csug9 .5/use.html#./use:s14 . Мне повезло со вспомогательными ключевыми словами в моем коде, потому что они уже были определены chezscheme, и я перепривязывал их в макросе. Все, что мне нужно было сделать в этом случае, это экспортировать их.   -  person Chris Vine    schedule 31.01.2019


Ответы (1)


В Chez Scheme некоторая семантика различается в зависимости от режима работы. Я назову эти режимы r6rs и repl.

R6RS:

  • программы (--program в командной строке)
  • библиотеки

ОТВЕТ:

  • скрипты (--script в командной строке)
  • файлы (с использованием процедуры загрузки или в качестве аргументов CLI без --program или --script)
  • и ответ, конечно

Для вашего конкретного вопроса вам необходимо определить и экспортировать идентификатор с именем in из forlib, если вы хотите, чтобы repl и сценарии соответствовали поведению библиотек и программ. Это может быть так же просто, как пустое определение, или вы можете создать синтаксическое определение, которое выдает ошибку при использовании вне тела макроса. Простой:

(library (forlib)
  (export for in)
  (import (rnrs))

  (define in)

  (define-syntax for ...)
)

Синтаксическое определение:

(library (forlib)
  (export for in)
  (import (rnrs))

  (define-syntax in
    (identifier-syntax
      (error #f "Misplaced aux syntax in")))

  (define-syntax for ...)
)

Оба работают нормально; используйте то, что вы предпочитаете.

Другие известные мне различия между режимами repl и r6rs связаны с тем, как они оценивают выражения. В режиме repl каждое выражение обрабатывается отдельно. То есть Chez читает выражение, оценивает это выражение и, возможно, печатает результаты. В режиме r6rs все содержимое файла оценивается как единое целое. Явно заявлено, что продолжением верхнего уровня режима repl является «чтение, оценка, возможно, печать и цикл», в то время как продолжением верхнего уровня режима r6rs является следующее выражение. Например, этот код будет вести себя по-разному при запуске как программы или скрипта:

(import (chezscheme))

(define println)

(printf "~x\n"
  (call/cc (lambda (k)
             (set! println k)
             1)))

(println 5)
(println 6)

Еще одно отличие состоит в том, что в режиме r6rs вы не можете определить функцию, а затем использовать ее в расширении макроса синтаксиса-регистра вне синтаксической кавычки.

(define ($two) 2)

(define-syntax two
  (lambda (x)
    (syntax-case x ()
      [_ ($two)])))
person gmw    schedule 15.01.2019
comment
Спасибо за отличный ответ. Это все, что мне нужно знать для работы с этим типом кода в Chez. Я также ценю некоторые различия между режимом REPL и режимом R6RS из-за индивидуального характера взаимодействия с REPL. Однако почему импорт макросов ведет себя по-разному в этих двух режимах, для меня до сих пор остается загадкой. - person Norbert Zeh; 21.01.2019