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 трябва да експортира „in“, както и „for“   -  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:

  • програми (--програма от командния ред)
  • библиотеки

REPL:

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

За конкретния ви въпрос трябва да дефинирате и експортирате идентификатор с име 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