Динамически требовать переменную фазы 1 (для синтаксиса) в Racket - PullRequest
2 голосов
/ 05 апреля 2019

Допустим, у меня есть какой-то модуль foo.rkt, который обеспечивает x на этапе 1.

#lang racket
(begin-for-syntax
  (define x 5)
  (provide x))

Когда вы запускаете (module->exports "foo.rkt"), вы возвращаетесь ((1 (x ()))), что означает, что предоставляется xна этапе 1 другие привязки не предоставляются.

Теперь в другом модуле я мог бы статически импортировать x во время выполнения, используя for-template:

#lang racket
(require (for-template "foo.rkt"))
x ; => 5

Но это статично,и так всегда будет.

Если бы это было на фазе 0, я мог бы использовать dynamic-require.Но кажется, что вы можете использовать dynamic-require только для запуска кода фазы 1, а не для получения каких-либо значений из этого запущенного кода.

Также есть dynamic-require-for-syntax, но мне так и не удалось работать.

Наконец, есть также namespace-require, но затем он переводит его в фазу 1 пространства имен, а не в фазу 0. Так что я мог бы сделать что-то вроде (eval '(begin-for-syntax (writeln x)), но это будет печатать только значение x, а неверните его.

Также есть namespace-variable-value, но он также, кажется, только возвращает значения на фазе 0.

Итак, есть ли возможность динамически (не статически) импортировать фазу 1?переменная из модуля?

1 Ответ

2 голосов
/ 05 апреля 2019

Да, есть способ, но он отвратительный.

Прежде всего, нам нужно создать базовое пространство имен, так что что-то вроде (define ns (make-base-namespace)) поможет.

Далее я бы порекомендовал использовать namespace-require/expansion-time вместо namespace-require.Он только создает экземпляр модуля (он же запускает только код фазы 1).

При этом x импортируется не в пространство имен, а на фазе 1, поэтому мы можем написать макрос, чтобы «переправить» егоот фазы 1 до фазы 0 через 3d синтаксис.

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

(eval '(define-syntax (cheater-x stx)
         #`'#,(datum->syntax #f x)))

И теперь вы можете просто сделать (eval 'cheater-x), чтобы получить значение x.

В целом ваш код должен выглядеть примерно так:

(define (dynamic-require-from-syntax module binding)
  (define ns (make-base-namespace))
  (parameterize ([current-namespace ns])
    (namespace-require 'racket)
    (namespace-require/expansion-time module)
    (eval `(define-syntax (cheater-x stx)
              #`'#,(datum->syntax #f ,binding)))
    (eval 'cheater-x)))
(dynamic-require-from-syntax "foo.rkt" 'x) ; => 5

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

...