Ракетка: Использование макроса между модулями - PullRequest
1 голос
/ 27 июля 2011

Чтобы научить себя более сложным макросам в ракетке, я приступил к созданию макроса для увеличения поля в изменяемой структуре:

(increment! instance name field) </p> <p>=> </p> <p>(set-name-field instance (get-name-field instance))

Я создал макрос, который работаети решил, что было бы полезно поделиться между несколькими модулями.К сожалению, поскольку структурные мутаторы не входят в область действия модуля, определяющего макрос, возникает ошибка раскрытия.

Ниже приведен надуманный пример, демонстрирующий проблему.Я хотел бы знать:

  1. Я написал код макроса в стиле идиоматических ракеток?Это правильный подход?

  2. Как я могу контролировать расширение макроса, чтобы он работал при наличии идентификаторов, не найденных в его исходном контексте?

Спасибо.

#lang racket/load

(module util racket

  (define-syntax increment!
    (lambda (stx)
      (syntax-case stx ()
        [(increment! s sn fn i)
         (with-syntax 
             ([set! (string->symbol 
                     (format "set-~a-~a!" (syntax-e #'sn) (syntax-e #'fn)))]
              [get (string->symbol 
                    (format "~a-~a" (syntax-e #'sn) (syntax-e #'fn)))])
           #'(set! s (+ i (get s))))]
        ;; default increment of 1
        [(increment! s sn fn) #'(increment! s sn fn 1)])))

  (provide increment!)
  )

(module bank racket
  (require 'util)
  (struct money (dollars pounds euros) #:mutable #:transparent)

  (let ([m (money 0 50 20)])
    (increment! m money pounds 100)
    (increment! m money dollars)
    m)
  )

(require 'bank)

В результате

развернуть: несвязанный идентификатор в модуле в: set-money-pounds!

1 Ответ

3 голосов
/ 27 июля 2011

Вы не можете сделать это. Проблема в том, что вы генерируете символы с правильными именами, но вы просто возвращаете символы как есть, что означает, что with-syntax дает им некоторый лексический контекст по умолчанию (и неправильный). Вместо этого вы должны использовать datum->syntax и дать ему правильный контекст.

Ниже приведена версия вашего кода, которая работает так, как вы ожидаете. Чтобы узнать больше об этом, см. Мой недавний пост в блоге на тему негигиенических макросов.

Однако это не надежное решение. Что происходит, когда сеттеры и геттеры имеют разные имена? Более надежным решением было бы использовать имя структуры и извлечь из него нужную информацию (во время синтаксиса, в макросе) - подробности см. В руководстве . Также полезно задавать вопросы об этом в списке рассылки , так как может быть лучший способ получить то, что вы хотите, или лучшее решение, если вы ищете какую-то функцию, подобную точечной нотации.

#lang racket/load

(module util racket
  (define-syntax increment!
    (lambda (stx)
      (syntax-case stx ()
        [(increment! s sn fn i)
         (let ([id (lambda (fmt)
                     (let ([str (format fmt (syntax-e #'sn) (syntax-e #'fn))])
                       (datum->syntax #'sn (string->symbol str))))])
           (with-syntax ([set! (id "set-~a-~a!")]
                         [get (id "~a-~a")])
             #'(set! s (+ i (get s)))))]
        ;; default increment of 1
        [(increment! s sn fn) #'(increment! s sn fn 1)])))
  (provide increment!))

(module bank racket
  (require 'util)
  (struct money (dollars pounds euros) #:mutable #:transparent)
  (let ([m (money 0 50 20)])
    (increment! m money pounds 100)
    (increment! m money dollars)
    m))

(require 'bank)
...