задавать! глобальный из макроса схемы? - PullRequest
4 голосов
/ 01 апреля 2011

Я пытаюсь написать оболочку для define, в которой хранятся переданные ей значения. Я подходил к нему детскими шагами (будучи новичком в Лиспе в целом и еще новее в Схеме), но наткнулся на стену.

В Racket я начинаю с:

> (require (lib "defmacro.ss"))
> (define-macro (mydefine thing definition)
      `(define ,thing ,definition))
> (mydefine a 9)
> a
9

Хорошо, это работает. Время сделать что-то в макросе, прежде чем возвращать s-выражения:

> (define-macro (mydefine thing definition)
    (display "This works")
    `(define ,thing ,definition))
> (mydefine a "bob")
This works
> a
"bob"

Nice. Но я не могу заставить себя установить глобальную переменную вместо отображения чего-то:

> (define *myglobal* null)
> (define-macro (mydefine thing definition)
    (set! *myglobal* "This does not")
    `(define ,thing ,definition))
> (mydefine a ":-(")
set!: cannot set identifier before its definition: *myglobal*

Будем весьма благодарны за любые предложения о том, как этого добиться.

Я подозреваю, что я пытаюсь плыть против течения здесь, либо возиться с глобальными переменными из макроса в Scheme, либо с помощью define-macro вместо изучения специфичного для Схемы синтаксиса для создания макроса.

Ответы [ 2 ]

6 голосов
/ 01 апреля 2011

Вы работаете против разделения фаз Racket - что означает, что каждая фаза (время выполнения и время компиляции) работают в разных мирах.Как отмечает Виджай, один из способов решить эту проблему - делать то, что вы хотите во время выполнения, но это, вероятно, не будет тем, что вам нужно в долгосрочной перспективе.Дело в том, что пробуя эти вещи обычно означает, что вы захотите хранить некоторую синтаксическую информацию на уровне времени компиляции.Например, скажем, что вы хотите сохранить имена всех ваших определенных имен, которые будут использоваться во втором макросе, который распечатает их все.Вы могли бы сделать это следующим образом (здесь я использую вменяемые макросы, define-macro - это устаревший хак, который не должен использоваться для реальной работы, вы можете посмотреть эти вещи в руководстве , а затемв ссылка ):

#lang racket
(define-for-syntax defined-names '())
(define-syntax (mydefine stx)
  (syntax-case stx ()
    [(_ name value)
     (identifier? #'name)
     (begin (set! defined-names (cons #'name defined-names))
            #'(define name value))]
    ;; provide the same syntactic sugar that `define' does
    [(_ (name . args) . body)
     #'(mydefine name (lambda args . body))]))

Обратите внимание, что defined-names определен на уровне синтаксиса, что означает, что обычный код времени выполнения не может ссылаться на него.Фактически, вы можете привязать его к другому значению на уровне времени выполнения, так как две привязки различны.Теперь, когда это сделано, вы можете написать макрос, который его использует - хотя defined-names недоступен во время выполнения, это простая привязка на уровне синтаксиса, поэтому:

(define-syntax (show-definitions stx)
  (syntax-case stx ()
    [(_) (with-syntax ([(name ...) (reverse defined-names)])
           #'(begin (printf "The global values are:\n")
                    (for ([sym (in-list '(name ...))]
                          [val (in-list (list name ...))])
                      (printf "  ~s = ~s\n" sym val))))]))
4 голосов
/ 01 апреля 2011

Оператор (set! *myglobal* "This does not") выполняется в среде преобразователя , а не в обычной среде.Поэтому он не может найти *myglobal.Нам нужно, чтобы оба выражения выполнялись в среде, где определено *myglobal*.

Вот одно из решений:

(define *defined-values* null)

(define-macro (mydefine thing definition)  
  `(begin
     (set! *defined-values* (cons ,definition *defined-values*))
     (define ,thing ,`(car *defined-values*))))


> (mydefine a 10)
> (mydefine b (+ 20 30))
> a
10
> b
50
> *defined-values*
(50 10)
> (define i 10)
> (mydefine a (begin (set! i (add1 i)) i)) ;; makes sure that `definition` 
                                           ;; is not evaluated twice.
> a
11

Если реализация Схемы не обеспечивает define-macro, но имеет define-syntax, mydefine можно определить как:

(define-syntax mydefine
  (syntax-rules ()
    ((_ thing definition)
     (begin       
       (set! *defined-values* (cons definition *defined-values*))
       (define thing (car *defined-values*))))))
...